m_Settings_Abstract ) { if ( empty( $class_name ) ) { return null; } if ( ! class_exists( $class_name ) ) { return null; } try { $form_settings_instance = new $class_name( $this, $module_id ); if ( ! $form_settings_instance instanceof Hustle_Provider_Form_Settings_Abstract ) { throw new Exception( $class_name . ' is not instanceof Hustle_Provider_Form_Settings_Abstract' ); } $this->provider_form_settings_instance[ $module_id ] = $form_settings_instance; } catch ( Exception $e ) { Hustle_Provider_Utils::maybe_log( $this->get_slug(), 'Failed to instantiate its _form_settings_instance', $e->getMessage() ); return null; } } return $this->provider_form_settings_instance[ $module_id ]; } /** * Executor of before_get_form_settings values, to be correctly mapped with form_setting instance for module_id. * * @since 4.0.0 * * @param array $values Settings to be stored. * @param string $module_id ID of the module to store the settings into. * * @return mixed */ final public function before_get_form_settings_values( $values, $module_id ) { $form_settings = $this->get_provider_form_settings( $module_id ); if ( $form_settings instanceof Hustle_Provider_Form_Settings_Abstract ) { if ( is_callable( array( $form_settings, 'before_get_form_settings_values' ) ) ) { return $form_settings->before_get_form_settings_values( $values ); } } return $values; } /** * Executor of before_save_form_settings_ values, to be correctly mapped with form_setting instance for module_id * * @since 4.0.0 * * @param array $values Settings to be stored. * @param string $module_id ID of the module to store the settings into. * * @return mixed */ final public function before_save_form_settings_values( $values, $module_id ) { $form_settings = $this->get_provider_form_settings( $module_id ); if ( $form_settings instanceof Hustle_Provider_Form_Settings_Abstract ) { if ( is_callable( array( $form_settings, 'before_save_form_settings_values' ) ) ) { return $form_settings->before_save_form_settings_values( $values ); } } return $values; } /** * Get Form Hooks of Addons * * @since 4.0.0 * * @param string $module_id Module ID. * @return Hustle_Provider_Form_Hooks_Abstract|null */ final public function get_addon_form_hooks( $module_id ) { if ( ! isset( $this->provider_form_hooks_instances[ $module_id ] ) || ! $this->provider_form_hooks_instances[ $module_id ] instanceof Hustle_Provider_Form_Hooks_Abstract ) { if ( empty( $this->form_hooks ) ) { return null; } if ( ! class_exists( $this->form_hooks ) ) { return null; } try { $classname = $this->form_hooks; $this->provider_form_hooks_instances[ $module_id ] = new $classname( $this, $module_id ); } catch ( Exception $e ) { Hustle_Provider_Utils::maybe_log( $this->get_slug(), 'Failed to instantiate its _addon_form_hooks_instance', $e->getMessage() ); return null; } } return $this->provider_form_hooks_instances[ $module_id ]; } /** * Gets the requested wizard. * * @since 3.0.5 * @since 4.0.0 $module_id param added. $is_close, $is_submit, $data_to_save params removed. * * @param array $steps Array with all the wizard's steps from the integration. * @param array $submitted_data Array with the submitted data. Softly sanitized by @see Opt_In_Utils::validate_and_sanitize_fields(). * @param string $module_id Module ID. * @param int $step Step from which the call is made. * * @return array|mixed */ private function get_wizard( $steps, $submitted_data, $module_id, $step = 0 ) { $total_steps = count( $steps ); $is_submit = ! empty( $submitted_data['hustle_is_submit'] ); // Validate callback, when its empty or not callable, mark as no wizard. if ( ! isset( $steps[ $step ]['callback'] ) || ! is_callable( $steps[ $step ]['callback'] ) ) { /* translators: provider's title */ return $this->get_empty_wizard( sprintf( __( 'No Settings available for %s', 'hustle' ), $this->get_title() ) ); } $wizard = call_user_func( $steps[ $step ]['callback'], $submitted_data, $is_submit, $module_id ); // A wizard to be able to processed by our application need to has at least `html` // which will be rendered or `redirect` which will be the url for redirect user to go to. if ( ! isset( $wizard['html'] ) && ! isset( $wizard['redirect'] ) ) { /* translators: provider's title */ return $this->get_empty_wizard( sprintf( __( 'No Settings available for %s', 'hustle' ), $this->get_title() ) ); } // Add 'hustle_is_submit' hidden input at the end. if ( isset( $wizard['html'] ) ) { $wizard['html'] = $wizard['html'] . $this->get_step_html_common_hidden_fields( $submitted_data ); } $wizard['opt_in_provider_current_step'] = $step; $wizard['opt_in_provider_count_step'] = $total_steps; $wizard['opt_in_provider_has_next_step'] = ( ( $step + 1 ) >= $total_steps ? false : true ); $wizard['opt_in_provider_has_prev_step'] = ( $step > 0 ? true : false ); // If ['data_to_save] is set on $wizard, that would mean the provider hasn't been apdapted // to 4.0. Save the data here if it's not updated so it keeps working. if ( isset( $wizard['data_to_save'] ) ) { $form_settings_instance = $this->get_provider_form_settings( $module_id ); $form_settings_instance->save_form_settings_values( $wizard['data_to_save'] ); } // Close the modal if... $do_close = ( // It's a submission. ! empty( $submitted_data['hustle_is_submit'] ) && // We're in the last step. ! $wizard['opt_in_provider_has_next_step'] && // And there are no errors. ( ! isset( $wizard['has_errors'] ) || ! $wizard['has_errors'] ) ); if ( $do_close ) { $wizard['is_close'] = true; } $wizard_default_values = array( 'has_errors' => false, 'is_close' => false, 'notification' => array(), 'size' => 'small', 'has_back' => false, ); foreach ( $wizard_default_values as $key => $wizard_default_value ) { if ( ! isset( $wizard[ $key ] ) ) { $wizard[ $key ] = $wizard_default_value; } } $wizard = apply_filters( 'hustle_get_integration_form_wizard', $wizard, $this, $submitted_data, $module_id, $steps, $step ); return $wizard; } /** * Gets empty wizard markup. * Helper to display a user friendly step when no settings are available. * * @since 3.0.5 * @param string $notice Message to be shown. * @return array */ public function get_empty_wizard( $notice ) { $notice_markup = '
'; $notice_markup .= ''; $notice_markup .= '

' . esc_html( $notice ) . '

'; $notice_markup .= '
'; return array( 'html' => $notice_markup, 'buttons' => array( 'close' => array( 'action' => 'close', 'data' => array(), 'markup' => '' . __( 'Close', 'hustle' ) . '', ), ), ); } /** * Override this function if your provider does something with the settings values. * Called when rendering settings form. * * @example transform, load from other storage ? * * @since 4.0.0 * * @param array $values Settings to be retrieved. * @return mixed */ public function before_get_settings_values( $values ) { return $values; } /** * Get settings value * * @see Hustle_Provider_Abstract::before_get_settings_values() * * @since 4.0.0 * @return array */ final public function get_settings_values() { $provider_slug = $this->get_slug(); $values = get_option( $this->get_settings_options_name(), array() ); /** * Filter the retrieved addon's settings values from db. * * @since 4.0.0 * * @param mixed $values */ $values = apply_filters( 'hustle_provider_' . $provider_slug . '_get_settings_values', $values ); return $values; } /** * Override this function if your provider does something with the settings values. * Called before saving the settings values to db. * * @example transform, save to other storage ? * * @since 4.0.0 * * @param array $values Settings to be saved. * @return mixed */ public function before_save_settings_values( $values ) { return $values; } /** * Save settings value * it's already hooked with * * @see Hustle_Provider_Abstract::before_save_settings_values() * * @since 4.0.0 * @param array $values Settings to be saved. */ final public function save_settings_values( $values ) { $provider_slug = $this->get_slug(); /** * Filter the settings values of the provider to be saved. * * `$provider_slug` is the slug of provider that will be saved. * Example : `mailchimp`, `zapier`, `etc` * * @since 4.0.0 * * @param mixed $values */ $values = apply_filters( 'hustle_provider_' . $provider_slug . '_save_settings_values', $values ); update_option( $this->get_settings_options_name(), $values ); } /** * Saves the settings for the given $global_multi_id. * * @since 4.0.0 * @uses Hustle_Provider_Abstract::save_settings_values() * * @param string $global_multi_id ID of the global instance of the provider. * @param array $values Settings to be stored. */ public function save_multi_settings_values( $global_multi_id, $values ) { $saved_settings = $this->get_settings_values(); if ( $this->is_allow_multi_on_global() ) { $settings_to_save = array_merge( $saved_settings, array( $global_multi_id => $values, ) ); } else { $settings_to_save = $values; } $this->save_settings_values( $settings_to_save ); } /** * Retrieves the settings for the provider's global instance. * * @since 4.2.0 * * @param boolean|string $global_multi_id ID of the global instance of the provider. False if not used. * @return array */ public function get_multi_settings_values( $global_multi_id = false ) { $settings = $this->get_settings_values(); if ( $this->is_allow_multi_on_global() ) { $settings = ( $global_multi_id && ! empty( $settings[ $global_multi_id ] ) ) ? $settings[ $global_multi_id ] : array(); } return $settings; } /** * Auto attach default admin hooks for provider * * @since 4.0.0 * @return bool */ final public function admin_hookable() { if ( $this->is_admin_hooked ) { return true; } $default_filters = array( 'hustle_provider_' . $this->get_slug() . '_save_settings_values' => array( array( $this, 'before_save_settings_values' ), 1 ), ); if ( $this->is_connected() ) { $default_filters[ 'hustle_provider_' . $this->get_slug() . '_save_form_settings_values' ] = array( array( $this, 'before_save_form_settings_values' ), 2 ); } foreach ( $default_filters as $filter => $default_filter ) { $function_to_add = $default_filter[0]; if ( is_callable( $function_to_add ) ) { $accepted_args = $default_filter[1]; add_filter( $filter, $function_to_add, 10, $accepted_args ); } } $this->is_admin_hooked = true; return true; } /** * Maintain hooks on all pages for providers. * * @since 4.0.0 * @return bool */ final public function global_hookable() { if ( $this->is_global_hooked ) { return true; } $default_filters = array( 'hustle_provider_' . $this->get_slug() . '_get_settings_values' => array( array( $this, 'before_get_settings_values' ), 1 ), ); if ( $this->is_connected() ) { $default_filters[ 'hustle_provider_' . $this->get_slug() . '_get_form_settings_values' ] = array( array( $this, 'before_get_form_settings_values' ), 2 ); } foreach ( $default_filters as $filter => $default_filter ) { $function_to_add = $default_filter[0]; if ( is_callable( $function_to_add ) ) { $accepted_args = $default_filter[1]; add_filter( $filter, $function_to_add, 10, $accepted_args ); } } $this->is_global_hooked = true; return true; } /** * Delete specific WP options for the current provider * * @since 4.0.1 */ public function remove_wp_options() { } /** * Gets the provider's data. * General function to get the provider's details from database based on a module_id and field key. * This method required an instance of Hustle_Module_Model. Now it accepts the module_id in order to prevent * third-party integrations from having to use new Hustle_Module_Model( $module_id ) just to use this method. * -Helper. * * @param int|Hustle_Module_Model $module_id The ID of the module from which the data will be retrieved. * @param string $field The field name in which the requested data is stored. * @param string $slug The slug of the provider which data is retrieved. * * @return string */ public static function get_provider_details( $module_id, $field, $slug ) { $details = ''; if ( is_object( $module_id ) && $module_id instanceof Hustle_Module_Model ) { $module = $module_id; } else { if ( ! ( $module_id instanceof Hustle_Module_Model ) || 0 === (int) $module_id ) { return $details; } $module = new Hustle_Module_Model( $module_id ); if ( is_wp_error( $module ) ) { return $details; } } if ( ! is_null( $module->content->email_services ) && isset( $module->content->email_services[ $slug ] ) && isset( $module->content->email_services[ $slug ][ $field ] ) ) { $details = $module->content->email_services[ $slug ][ $field ]; } return $details; } /** * Process the return value of an external redirect. * Also, return the behavior to have in the global integrations page. * Useful for handling oAuth. * * @since 4.0.2 * * @return array */ public function process_external_redirect() { return array(); } /** * Updates provider's db option with the new value. * * @uses update_option * @param string $option_key Name of the provider's option to be stored. * @param mixed $option_value Value to be stored. * @return bool */ public function update_provider_option( $option_key, $option_value ) { return update_option( $this->get_slug() . '_' . $option_key, $option_value ); } /** * Retrieves provider's option from db. * * @uses get_option * @param string $option_key Name of the option to retrieve. * @param mixed $default Value to return if the option wasn't found. * @return mixed */ public function get_provider_option( $option_key, $default ) { return get_option( $this->get_slug() . '_' . $option_key, $default ); } /** * Delete provider's option from db. * * @since 4.0.1 * @uses delete_option * @param string $option_key Name of the option to be deleted. * @return bool */ public function delete_provider_option( $option_key ) { return delete_option( $this->get_slug() . '_' . $option_key ); } /** * Like form_settings_wizards(), but for global settings. * Should be overridden in order to show a wizard in the global settings. * * @since 4.0.0 * @return array */ public function settings_wizards() { return array(); } /** * Get a stored setting. * Handles global_multi_id if the id is passed. * * @since 4.0.0 * * @param string $setting_name Name of the setting to be retrieved. * @param mixed $default Value to return if the setting wasn't found. * @param string $global_multi_id ID of the global instance of the provider. * @return mixed */ public function get_setting( $setting_name, $default = false, $global_multi_id = false ) { $setting_values = $this->get_settings_values(); $retrieved_setting = $default; if ( $global_multi_id ) { if ( isset( $setting_values[ $global_multi_id ] ) ) { $account = $setting_values[ $global_multi_id ]; if ( isset( $account[ $setting_name ] ) ) { $retrieved_setting = $account[ $setting_name ]; } } } else { if ( isset( $setting_values[ $setting_name ] ) ) { $retrieved_setting = $setting_values[ $setting_name ]; } } return $retrieved_setting; } /** * Get the first found global actie connection. * * @since 4.0.0 * @return false|Hustle_Provider_Abstract */ public function find_one_global_active_connection() { $setting_values = $this->get_settings_values(); foreach ( $setting_values as $multi_id => $setting ) { if ( true === $this->settings_are_completed( $multi_id ) ) { return $setting; } } return false; } /** * Override this function to generate your multiple id for form settings. * Default is uniqid. * * @since 4.0.0 * @return string */ public function generate_multi_id() { return uniqid( '', true ); } /** * Get an array with the id of the multiple instances of a provider in a module. * * @since 4.0.0 * * @param string $module_id ID of the module. * @return array */ private function get_form_settings_multi_ids( $module_id ) { $addon_slug = $this->get_slug(); $addon = $this; $multi_ids = array(); $form_settings_instance = $this->get_provider_form_settings( $module_id ); if ( $this->is_allow_multi_on_form() && ! is_null( $form_settings_instance ) && $form_settings_instance instanceof Hustle_Provider_Form_Settings_Abstract ) { $multi_ids = $form_settings_instance->get_multi_ids(); } return $multi_ids; } /** * Get the globally connected accounts of this integration. * Returned as an array such as * ( * array( * 'id' => {account ID}, * 'label' => {account name} * ), * array( * 'id' => {account 2 ID}, * 'label' => {account 2 name} * ) * ) * * @since 4.0.0 * @return array */ public function get_global_multi_ids() { $multi_ids = array(); $saved_settings = $this->get_settings_values(); foreach ( $saved_settings as $key => $value ) { $multi_ids[] = array( 'id' => $key, // If 'name' exists, use it instead. 'label' => isset( $value['name'] ) ? $value['name'] : $key, ); } return $multi_ids; } /** * Get existing global multi id or generate a new one * * @param array $submitted_data Submitted data. * @return string */ public function get_global_multi_id( $submitted_data ) { $id = isset( $submitted_data['global_multi_id'] ) ? $submitted_data['global_multi_id'] : $this->generate_multi_id(); return $id; } /** * Get the current data for the integration. * If not submitted, get it from the stored settings. * Handles multi_id settings. * * @since 4.0.0 * * @param array $current_data Data that's currently stored. * @param array $submitted_data Incoming data. * @return array */ protected function get_current_data( $current_data, $submitted_data ) { $global_multi_id = isset( $submitted_data['global_multi_id'] ) ? $submitted_data['global_multi_id'] : false; $saved_settings = $this->get_settings_values(); foreach ( $current_data as $key => $current_field ) { if ( isset( $submitted_data[ $key ] ) ) { $current_data[ $key ] = $submitted_data[ $key ]; } elseif ( isset( $saved_settings[ $key ] ) && ! $this->is_allow_multi_on_global() ) { $current_data[ $key ] = $saved_settings[ $key ]; } elseif ( $global_multi_id && isset( $saved_settings[ $global_multi_id ][ $key ] ) ) { $current_data[ $key ] = $saved_settings[ $global_multi_id ][ $key ]; } } return $current_data; } /** * Get hidden fields that are common among the providers. * * @since 4.0.0 * * @param array $submitted_data Submitted data. * @return string */ protected function get_step_html_common_hidden_fields( $submitted_data ) { $options = array( array( 'name' => 'hustle_is_submit', 'type' => 'hidden', 'value' => '1', ), ); if ( $this->is_allow_multi_on_form() ) { $options[] = array( 'name' => 'multi_id', 'type' => 'hidden', 'value' => isset( $submitted_data['multi_id'] ) ? $submitted_data['multi_id'] : $this->generate_multi_id(), ); } if ( $this->is_allow_multi_on_global() ) { $options[] = array( 'name' => 'global_multi_id', 'type' => 'hidden', 'value' => isset( $submitted_data['global_multi_id'] ) ? $submitted_data['global_multi_id'] : $this->generate_multi_id(), ); } $html = Hustle_Provider_Utils::get_html_for_options( $options ); $html = apply_filters( 'hustle_providers_admin_add_common_hidden_fields', $html ); return $html; } /** * In version 3.0 provider details like API key and URL were stored at module level, * now they are stored globally to avoid duplication. * * This method addresses this difference. * * @param Hustle_Module_Model $module Current module. * @param Object $old_module Old module. * * @return bool */ public function migrate_30( $module, $old_module ) { $v3_provider = ! empty( $old_module->meta['content']['email_services'][ $this->get_slug() ] ) ? $old_module->meta['content']['email_services'][ $this->get_slug() ] : false; if ( empty( $v3_provider ) || $this->get_30_provider_mappings() === false ) { // Nothing to migrate. return false; } $v3_provider_active = '1' === $v3_provider['enabled']; // If the provider doesn't already exist globally, add it. $global_multi_id = $this->get_30_migrated_provider( $v3_provider ); if ( empty( $global_multi_id ) ) { $global_multi_id = $this->generate_multi_id(); $this->save_multi_settings_values( $global_multi_id, $this->map_30_provider( $v3_provider ) ); // Activate the addon. Hustle_Providers::get_instance()->activate_addon( $this->get_slug() ); } // Link the provider to the module. if ( $v3_provider_active ) { $module_provider_link = $this->strip_30_global_provider_settings( $v3_provider ); $module_provider_link['selected_global_multi_id'] = $global_multi_id; $module->set_provider_settings( $this->get_slug(), $module_provider_link ); } return true; } /** * Map the provider's field from the old settings to the new ones. * * @since 4.0.0 * @param array $v3_provider Old settings of the provider. */ private function map_30_provider( $v3_provider ) { $v4_provider = array(); $mappings = $this->get_30_provider_mappings(); foreach ( $mappings as $v3_index => $v4_index ) { if ( isset( $v3_provider[ $v3_index ] ) ) { $v4_provider[ $v4_index ] = $v3_provider[ $v3_index ]; } } return $v4_provider; } /** * Gets the provider's map for the 3.x to 4.x migration. * * @since 4.0.0 * @return false|array */ protected function get_30_provider_mappings() { return false; } /** * If a provider has already been migrated from 3.0 this method will return its id. * * @param array $v3_provider Old settings of the provider. * * @return bool|string Global multi ID */ private function get_30_migrated_provider( $v3_provider ) { $v40_providers = $this->get_settings_values(); $mapped_40_provider = $this->map_30_provider( $v3_provider ); foreach ( $v40_providers as $global_multi_id => $v40_provider ) { if ( $v40_provider === $mapped_40_provider ) { return $global_multi_id; } } return false; } /** * Strips unused old settings for the provider. * * @since 4.0.0 * * @param array $v3_provider Old provider's settings. * @return array */ private function strip_30_global_provider_settings( $v3_provider ) { $copy = array(); $global_provider_settings = array_merge( array( 'enabled', 'optin_provider_name', 'desc' ), array_keys( $this->get_30_provider_mappings() ) ); foreach ( $v3_provider as $item => $value ) { if ( in_array( $item, $global_provider_settings, true ) ) { continue; } $copy[ $item ] = $value; } return $copy; } /** * If a provider fails to connect, * returns a generic message. * * @since 4.0.0 * * @return string error message */ protected function provider_connection_falied() { /* translators: provider's title */ $error_message = sprintf( __( "We couldn't connect to your %s account. Please resolve the errors below and try again.", 'hustle' ), $this->title ); return $error_message; } }