433 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Integration API
 | 
						|
 *
 | 
						|
 * @link https://contactform7.com/integration-with-external-apis/
 | 
						|
 */
 | 
						|
 | 
						|
class WPCF7_Integration {
 | 
						|
 | 
						|
	private static $instance;
 | 
						|
 | 
						|
	private $services = array();
 | 
						|
	private $categories = array();
 | 
						|
 | 
						|
	private function __construct() {}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns initially supported service categories.
 | 
						|
	 *
 | 
						|
	 * @return array Service categories.
 | 
						|
	 */
 | 
						|
	public static function get_builtin_categories() {
 | 
						|
		return array(
 | 
						|
			'spam_protection' => __( 'Spam protection', 'contact-form-7' ),
 | 
						|
			'email_marketing' => __( 'Email marketing', 'contact-form-7' ),
 | 
						|
			'payments' => __( 'Payments', 'contact-form-7' ),
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns the singleton instance of this class.
 | 
						|
	 *
 | 
						|
	 * @return WPCF7_Integration The instance.
 | 
						|
	 */
 | 
						|
	public static function get_instance() {
 | 
						|
		if ( empty( self::$instance ) ) {
 | 
						|
			self::$instance = new self;
 | 
						|
			self::$instance->categories = self::get_builtin_categories();
 | 
						|
		}
 | 
						|
 | 
						|
		return self::$instance;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Adds a service to the services list.
 | 
						|
	 */
 | 
						|
	public function add_service( $name, WPCF7_Service $service ) {
 | 
						|
		$name = sanitize_key( $name );
 | 
						|
 | 
						|
		if ( empty( $name )
 | 
						|
		or isset( $this->services[$name] ) ) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		$this->services[$name] = $service;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Adds a service category to the categories list.
 | 
						|
	 */
 | 
						|
	public function add_category( $name, $title ) {
 | 
						|
		$name = sanitize_key( $name );
 | 
						|
 | 
						|
		if ( empty( $name )
 | 
						|
		or isset( $this->categories[$name] ) ) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		$this->categories[$name] = $title;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns true if a service with the name exists in the services list.
 | 
						|
	 *
 | 
						|
	 * @param string $name The name of service to search.
 | 
						|
	 */
 | 
						|
	public function service_exists( $name = '' ) {
 | 
						|
		if ( '' == $name ) {
 | 
						|
			return (bool) count( $this->services );
 | 
						|
		} else {
 | 
						|
			return isset( $this->services[$name] );
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns a service object with the name.
 | 
						|
	 *
 | 
						|
	 * @param string $name The name of service.
 | 
						|
	 * @return WPCF7_Service|bool The service object if it exists,
 | 
						|
	 *                            false otherwise.
 | 
						|
	 */
 | 
						|
	public function get_service( $name ) {
 | 
						|
		if ( $this->service_exists( $name ) ) {
 | 
						|
			return $this->services[$name];
 | 
						|
		} else {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Prints services list.
 | 
						|
	 */
 | 
						|
	public function list_services( $args = '' ) {
 | 
						|
		$args = wp_parse_args( $args, array(
 | 
						|
			'include' => array(),
 | 
						|
		) );
 | 
						|
 | 
						|
		$singular = false;
 | 
						|
		$services = (array) $this->services;
 | 
						|
 | 
						|
		if ( ! empty( $args['include'] ) ) {
 | 
						|
			$services = array_intersect_key( $services,
 | 
						|
				array_flip( (array) $args['include'] )
 | 
						|
			);
 | 
						|
 | 
						|
			if ( 1 == count( $services ) ) {
 | 
						|
				$singular = true;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if ( empty( $services ) ) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		$action = wpcf7_current_action();
 | 
						|
 | 
						|
		foreach ( $services as $name => $service ) {
 | 
						|
			$cats = array_intersect_key( $this->categories,
 | 
						|
				array_flip( $service->get_categories() )
 | 
						|
			);
 | 
						|
?>
 | 
						|
<div class="card<?php echo $service->is_active() ? ' active' : ''; ?>" id="<?php echo esc_attr( $name ); ?>">
 | 
						|
<?php $service->icon(); ?>
 | 
						|
<h2 class="title"><?php echo esc_html( $service->get_title() ); ?></h2>
 | 
						|
<div class="infobox">
 | 
						|
<?php echo esc_html( implode( ', ', $cats ) ); ?>
 | 
						|
<br />
 | 
						|
<?php $service->link(); ?>
 | 
						|
</div>
 | 
						|
<br class="clear" />
 | 
						|
 | 
						|
<div class="inside">
 | 
						|
<?php
 | 
						|
			if ( $singular ) {
 | 
						|
				$service->display( $action );
 | 
						|
			} else {
 | 
						|
				$service->display();
 | 
						|
			}
 | 
						|
?>
 | 
						|
</div>
 | 
						|
</div>
 | 
						|
<?php
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Abstract class for services.
 | 
						|
 *
 | 
						|
 * Only instances of this class's subclasses are allowed to be
 | 
						|
 * listed on the Integration page.
 | 
						|
 */
 | 
						|
abstract class WPCF7_Service {
 | 
						|
 | 
						|
	abstract public function get_title();
 | 
						|
	abstract public function is_active();
 | 
						|
 | 
						|
 | 
						|
	public function get_categories() {
 | 
						|
		return array();
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function icon() {
 | 
						|
		return '';
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function link() {
 | 
						|
		return '';
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function load( $action = '' ) {
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function display( $action = '' ) {
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function admin_notice( $message = '' ) {
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Class for services that use OAuth.
 | 
						|
 *
 | 
						|
 * While this is not an abstract class, subclassing this class for
 | 
						|
 * your aim is advised.
 | 
						|
 */
 | 
						|
class WPCF7_Service_OAuth2 extends WPCF7_Service {
 | 
						|
 | 
						|
	protected $client_id = '';
 | 
						|
	protected $client_secret = '';
 | 
						|
	protected $access_token = '';
 | 
						|
	protected $refresh_token = '';
 | 
						|
	protected $authorization_endpoint = 'https://example.com/authorization';
 | 
						|
	protected $token_endpoint = 'https://example.com/token';
 | 
						|
 | 
						|
 | 
						|
	public function get_title() {
 | 
						|
		return '';
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function is_active() {
 | 
						|
		return ! empty( $this->refresh_token );
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function save_data() {
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function reset_data() {
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function get_redirect_uri() {
 | 
						|
		return admin_url();
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function menu_page_url( $args = '' ) {
 | 
						|
		return menu_page_url( 'wpcf7-integration', false );
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	public function load( $action = '' ) {
 | 
						|
		if ( 'auth_redirect' === $action ) {
 | 
						|
			$code = $_GET['code'] ?? '';
 | 
						|
 | 
						|
			if ( $code ) {
 | 
						|
				$this->request_token( $code );
 | 
						|
			}
 | 
						|
 | 
						|
			if ( ! empty( $this->access_token ) ) {
 | 
						|
				$message = 'success';
 | 
						|
			} else {
 | 
						|
				$message = 'failed';
 | 
						|
			}
 | 
						|
 | 
						|
			wp_safe_redirect( $this->menu_page_url(
 | 
						|
				array(
 | 
						|
					'action' => 'setup',
 | 
						|
					'message' => $message,
 | 
						|
				)
 | 
						|
			) );
 | 
						|
 | 
						|
			exit();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function authorize( $scope = '' ) {
 | 
						|
		$endpoint = add_query_arg(
 | 
						|
			array(
 | 
						|
				'response_type' => 'code',
 | 
						|
				'client_id' => $this->client_id,
 | 
						|
				'redirect_uri' => urlencode( $this->get_redirect_uri() ),
 | 
						|
				'scope' => $scope,
 | 
						|
			),
 | 
						|
			$this->authorization_endpoint
 | 
						|
		);
 | 
						|
 | 
						|
		if ( wp_redirect( sanitize_url( $endpoint ) ) ) {
 | 
						|
			exit();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function get_http_authorization_header( $scheme = 'basic' ) {
 | 
						|
		$scheme = strtolower( trim( $scheme ) );
 | 
						|
 | 
						|
		switch ( $scheme ) {
 | 
						|
			case 'bearer':
 | 
						|
				return sprintf( 'Bearer %s', $this->access_token );
 | 
						|
			case 'basic':
 | 
						|
			default:
 | 
						|
				return sprintf( 'Basic %s',
 | 
						|
					base64_encode( $this->client_id . ':' . $this->client_secret )
 | 
						|
				);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function request_token( $authorization_code ) {
 | 
						|
		$endpoint = add_query_arg(
 | 
						|
			array(
 | 
						|
				'code' => $authorization_code,
 | 
						|
				'redirect_uri' => urlencode( $this->get_redirect_uri() ),
 | 
						|
				'grant_type' => 'authorization_code',
 | 
						|
			),
 | 
						|
			$this->token_endpoint
 | 
						|
		);
 | 
						|
 | 
						|
		$request = array(
 | 
						|
			'headers' => array(
 | 
						|
				'Authorization' => $this->get_http_authorization_header( 'basic' ),
 | 
						|
			),
 | 
						|
		);
 | 
						|
 | 
						|
		$response = wp_remote_post( sanitize_url( $endpoint ), $request );
 | 
						|
		$response_code = (int) wp_remote_retrieve_response_code( $response );
 | 
						|
		$response_body = wp_remote_retrieve_body( $response );
 | 
						|
		$response_body = json_decode( $response_body, true );
 | 
						|
 | 
						|
		if ( WP_DEBUG and 400 <= $response_code ) {
 | 
						|
			$this->log( $endpoint, $request, $response );
 | 
						|
		}
 | 
						|
 | 
						|
		if ( 401 == $response_code ) { // Unauthorized
 | 
						|
			$this->access_token = null;
 | 
						|
			$this->refresh_token = null;
 | 
						|
		} else {
 | 
						|
			if ( isset( $response_body['access_token'] ) ) {
 | 
						|
				$this->access_token = $response_body['access_token'];
 | 
						|
			} else {
 | 
						|
				$this->access_token = null;
 | 
						|
			}
 | 
						|
 | 
						|
			if ( isset( $response_body['refresh_token'] ) ) {
 | 
						|
				$this->refresh_token = $response_body['refresh_token'];
 | 
						|
			} else {
 | 
						|
				$this->refresh_token = null;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		$this->save_data();
 | 
						|
 | 
						|
		return $response;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function refresh_token() {
 | 
						|
		$endpoint = add_query_arg(
 | 
						|
			array(
 | 
						|
				'refresh_token' => $this->refresh_token,
 | 
						|
				'grant_type' => 'refresh_token',
 | 
						|
			),
 | 
						|
			$this->token_endpoint
 | 
						|
		);
 | 
						|
 | 
						|
		$request = array(
 | 
						|
			'headers' => array(
 | 
						|
				'Authorization' => $this->get_http_authorization_header( 'basic' ),
 | 
						|
			),
 | 
						|
		);
 | 
						|
 | 
						|
		$response = wp_remote_post( sanitize_url( $endpoint ), $request );
 | 
						|
		$response_code = (int) wp_remote_retrieve_response_code( $response );
 | 
						|
		$response_body = wp_remote_retrieve_body( $response );
 | 
						|
		$response_body = json_decode( $response_body, true );
 | 
						|
 | 
						|
		if ( WP_DEBUG and 400 <= $response_code ) {
 | 
						|
			$this->log( $endpoint, $request, $response );
 | 
						|
		}
 | 
						|
 | 
						|
		if ( 401 == $response_code ) { // Unauthorized
 | 
						|
			$this->access_token = null;
 | 
						|
			$this->refresh_token = null;
 | 
						|
		} else {
 | 
						|
			if ( isset( $response_body['access_token'] ) ) {
 | 
						|
				$this->access_token = $response_body['access_token'];
 | 
						|
			} else {
 | 
						|
				$this->access_token = null;
 | 
						|
			}
 | 
						|
 | 
						|
			if ( isset( $response_body['refresh_token'] ) ) {
 | 
						|
				$this->refresh_token = $response_body['refresh_token'];
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		$this->save_data();
 | 
						|
 | 
						|
		return $response;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function remote_request( $url, $request = array() ) {
 | 
						|
		static $refreshed = false;
 | 
						|
 | 
						|
		$request = wp_parse_args( $request, array() );
 | 
						|
 | 
						|
		$request['headers'] = array_merge(
 | 
						|
			$request['headers'],
 | 
						|
			array(
 | 
						|
				'Authorization' => $this->get_http_authorization_header( 'bearer' ),
 | 
						|
			)
 | 
						|
		);
 | 
						|
 | 
						|
		$response = wp_remote_request( sanitize_url( $url ), $request );
 | 
						|
 | 
						|
		if ( 401 === wp_remote_retrieve_response_code( $response )
 | 
						|
		and ! $refreshed ) {
 | 
						|
			$this->refresh_token();
 | 
						|
			$refreshed = true;
 | 
						|
 | 
						|
			$response = $this->remote_request( $url, $request );
 | 
						|
		}
 | 
						|
 | 
						|
		return $response;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	protected function log( $url, $request, $response ) {
 | 
						|
		wpcf7_log_remote_request( $url, $request, $response );
 | 
						|
	}
 | 
						|
 | 
						|
}
 |