Skip to content
Snippets Groups Projects
Plugin.php 8.43 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    /**
     * Main plugin class.
     *
     * @since 0.1
     */
    
    namespace CiviCRM_WP_REST;
    
    use CiviCRM_WP_REST\Civi\Mailing_Hooks;
    
    class Plugin {
    
    	/**
    	 * Constructor.
    	 *
    	 * @since 0.1
    	 */
    	public function __construct() {
    
    		$this->register_hooks();
    
    		$this->setup_objects();
    
    	}
    
    	/**
    	 * Register hooks.
    	 *
    	 * @since 1.0
    	 */
    	protected function register_hooks() {
    
    		add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
    
    		add_filter( 'rest_pre_dispatch', [ $this, 'bootstrap_civi' ], 10, 3 );
    
    
    		add_filter( 'rest_post_dispatch',  [ $this, 'maybe_reset_wp_timezone' ], 10, 3);
    
    
    	}
    
    	/**
    	 * Bootstrap CiviCRM when hitting a the 'civicrm' namespace.
    	 *
    	 * @since 0.1
    	 * @param mixed $result
    	 * @param WP_REST_Server $server REST server instance
    	 * @param WP_REST_Request $request The request
    	 * @return mixed $result
    	 */
    	public function bootstrap_civi( $result, $server, $request ) {
    
    
    		if ( false !== strpos( $request->get_route(), 'civicrm' ) ) {
    
    			$this->maybe_set_user_timezone( $request );
    
    			civi_wp()->initialize();
    
    
    			// rest calls need a wp user, do login
    			if ( false !== strpos( $request->get_route(), 'rest' ) ) {
    
    				$logged_in_wp_user = $this->do_user_login( $request );
    
    				// return error
    				if ( is_wp_error( $logged_in_wp_user ) ) {
    					return $logged_in_wp_user;
    				}
    			}
    
    
    
    		return $result;
    
    	}
    
    	/**
    	 * Setup objects.
    	 *
    	 * @since 0.1
    	 */
    	private function setup_objects() {
    
    
    		/**
     		 * Filter to replace the mailing tracking URLs.
     		 *
     		 * @since 0.1
     		 * @param bool $replace_mailing_tracking_urls
     		 */
     		$replace_mailing_tracking_urls = apply_filters( 'civi_wp_rest/plugin/replace_mailing_tracking_urls', false );
    
     		// keep CIVICRM_WP_REST_REPLACE_MAILING_TRACKING for backwards compatibility
     		if (
     			$replace_mailing_tracking_urls
     			|| ( defined( 'CIVICRM_WP_REST_REPLACE_MAILING_TRACKING' ) && CIVICRM_WP_REST_REPLACE_MAILING_TRACKING )
     		) {
    
    			// register mailing hooks
    			$mailing_hooks = ( new Mailing_Hooks )->register_hooks();
    
    		}
    
    	}
    
    	/**
    	 * Registers Rest API routes.
    	 *
    	 * @since 0.1
    	 */
    	public function register_rest_routes() {
    
    		// rest endpoint
    		$rest_controller = new Controller\Rest;
    		$rest_controller->register_routes();
    
    		// url controller
    		$url_controller = new Controller\Url;
    		$url_controller->register_routes();
    
    		// open controller
    		$open_controller = new Controller\Open;
    		$open_controller->register_routes();
    
    		// authorizenet controller
    		$authorizeIPN_controller = new Controller\AuthorizeIPN;
    		$authorizeIPN_controller->register_routes();
    
    		// paypal controller
    		$paypalIPN_controller = new Controller\PayPalIPN;
    		$paypalIPN_controller->register_routes();
    
    		// pxpay controller
    		$paypalIPN_controller = new Controller\PxIPN;
    		$paypalIPN_controller->register_routes();
    
    		// civiconnect controller
    		$cxn_controller = new Controller\Cxn;
    		$cxn_controller->register_routes();
    
    		// widget controller
    		$widget_controller = new Controller\Widget;
    		$widget_controller->register_routes();
    
    		// soap controller
    		$soap_controller = new Controller\Soap;
    		$soap_controller->register_routes();
    
    		/**
    		 * Opportunity to add more rest routes.
    		 *
    		 * @since 0.1
    		 */
    		do_action( 'civi_wp_rest/plugin/rest_routes_registered' );
    
    	}
    
    
    	/**
    	 * Sets the timezone to the users timezone when
    	 * calling the civicrm/v3/rest endpoint.
    	 *
    	 * @since 0.1
    	 * @param WP_REST_Request $request The request
    	 */
    	private function maybe_set_user_timezone( $request ) {
    
    		if ( $request->get_route() != '/civicrm/v3/rest' ) return;
    
    		$timezones = [
    			'wp_timezone' => date_default_timezone_get(),
    			'user_timezone' => get_option( 'timezone_string', false )
    		];
    
    		// filter timezones
    		add_filter( 'civi_wp_rest/plugin/timezones', function() use ( $timezones ) {
    
    			return $timezones;
    
    		} );
    
    		if ( empty( $timezones['user_timezone'] ) ) return;
    
    		/**
    		 * CRM-12523
    		 * CRM-18062
    		 * CRM-19115
    		 */
    		date_default_timezone_set( $timezones['user_timezone'] );
    		\CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
    
    	}
    
    	/**
    	 * Resets the timezone to the original WP
    	 * timezone after calling the civicrm/v3/rest endpoint.
    	 *
    	 * @since 0.1
    	 * @param mixed $result
    	 * @param WP_REST_Server $server REST server instance
    	 * @param WP_REST_Request $request The request
    	 * @return mixed $result
    	 */
    	public function maybe_reset_wp_timezone( $result, $server, $request ) {
    
    
    Andrei Mondoc's avatar
    Andrei Mondoc committed
    		if ( $request->get_route() != '/civicrm/v3/rest' ) return $result;
    
    
    		$timezones = apply_filters( 'civi_wp_rest/plugin/timezones', null );
    
    		if ( empty( $timezones['wp_timezone'] ) ) return $result;
    
    		// reset wp timezone
    		date_default_timezone_set( $timezones['wp_timezone'] );
    
    		return $result;
    
    	}
    
    
    	/**
    	 * Performs the necessary checks and
    	 * data retrieval to login a WordPress user.
    	 *
    	 * @since 0.1
    	 * @param \WP_REST_Request $request The request
    	 * @return \WP_User|\WP_Error|void $logged_in_wp_user The logged in WordPress user object, \Wp_Error, or nothing
    	 */
    	public function do_user_login( $request ) {
    
    		/**
    		 * Filter and opportunity to bypass
    		 * the default user login.
    		 *
    		 * @since 0.1
    		 * @param bool $login
    		 */
    		$logged_in = apply_filters( 'civi_wp_rest/plugin/do_user_login', false, $request );
    
    		if ( $logged_in ) return;
    
    		// default login based on contact's api_key
    		if ( ! ( new Controller\Rest )->is_valid_api_key( $request ) ) {
    			return new \WP_Error(
    				'civicrm_rest_api_error',
    				__( 'Missing or invalid param "api_key".', 'civicrm' )
    			);
    		}
    
    		$contact_id = \CRM_Core_DAO::getFieldValue(
    			'CRM_Contact_DAO_Contact',
    			$request->get_param( 'api_key' ),
    			'id',
    			'api_key'
    		);
    
    		$wp_user = $this->get_wp_user( $contact_id );
    
    		if ( is_wp_error( $wp_user ) ) {
    			return $wp_user;
    		}
    
    		return $this->login_wp_user( $wp_user, $request );
    
    	}
    
    	/**
    	 * Get WordPress user data.
    	 *
    	 * @since 0.1
    	 * @param int $contact_id The contact id
    	 * @return WP_User|WP_Error $user The WordPress user data or WP_Error object
    	 */
    	public function get_wp_user( int $contact_id ) {
    
    		try {
    
    			// Call API.
    			$uf_match = civicrm_api3( 'UFMatch', 'getsingle', [
    				'contact_id' => $contact_id,
    				'domain_id' => $this->get_civi_domain_id(),
    			] );
    
    		} catch ( \CiviCRM_API3_Exception $e ) {
    
    			return new \WP_Error(
    				'civicrm_rest_api_error',
    				__( 'A WordPress user must be associated with the contact for the provided API key.', 'civicrm' )
    			);
    
    		}
    
    		// filter uf_match
    		add_filter( 'civi_wp_rest/plugin/uf_match', function() use ( $uf_match ) {
    
    			return ! empty( $uf_match ) ? $uf_match : null;
    
    		} );
    
    		return get_userdata( $uf_match['uf_id'] );
    
    	}
    
    	/**
    	 * Logs in the WordPress user, and
    	 * syncs it with it's CiviCRM contact.
    	 *
    	 * @since 0.1
    	 * @param \WP_User $user The WordPress user object
    	 * @param \WP_REST_Request|null $request The request object or null
    	 * @return \WP_User|void $wp_user The logged in WordPress user object or nothing
    	 */
    	public function login_wp_user( \WP_User $wp_user, $request = null ) {
    
    		/**
    		 * Filter the user about to be logged in.
    		 *
    		 * @since 0.1
    		 * @param \WP_User $user The WordPress user object
    		 * @param \WP_REST_Request|null $request The request object or null
    		 */
    		$wp_user = apply_filters( 'civi_wp_rest/plugin/wp_user_login', $wp_user, $request );
    
    		wp_set_current_user( $wp_user->ID, $wp_user->user_login );
    
    		wp_set_auth_cookie( $wp_user->ID );
    
    		do_action( 'wp_login', $wp_user->user_login, $wp_user );
    
    		$this->set_civi_user_session( $wp_user );
    
    		return $wp_user;
    
    	}
    
    	/**
    	 * Sets the necessary user
    	 * session variables for CiviCRM.
    	 *
    	 * @since 0.1
    	 * @param \WP_User $wp_user The WordPress user
    	 * @return void
    	 */
    	public function set_civi_user_session( $wp_user ): void {
    
    		$uf_match = apply_filters( 'civi_wp_rest/plugin/uf_match', null );
    
    		if ( ! $uf_match ) {
    
    			// Call API.
    			$uf_match = civicrm_api3( 'UFMatch', 'getsingle', [
    				'uf_id' => $wp_user->ID,
    				'domain_id' => $this->get_civi_domain_id(),
    			] );
    		}
    
    		// Set necessary session variables.
    		$session = \CRM_Core_Session::singleton();
    		$session->set( 'ufID', $wp_user->ID );
    		$session->set( 'userID', $uf_match['contact_id'] );
    
    	}
    
    	/**
    	 * Retrieves the CiviCRM domain_id.
    	 *
    	 * @since 0.1
    	 * @return int $domain_id The domain id
    	 */
    	public function get_civi_domain_id(): int {
    
    		// Get CiviCRM domain group ID from constant, if set.
    		$domain_id = defined( 'CIVICRM_DOMAIN_ID' ) ? CIVICRM_DOMAIN_ID : 0;
    
    		// If this fails, get it from config.
    		if ( $domain_id === 0 ) {
    			$domain_id = \CRM_Core_Config::domainID();
    		}
    
    		return $domain_id;
    
    	}