<?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 ) { 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; } }