AlkantarClanX12

Your IP : 216.73.217.24


Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/popup-maker/classes/Services/
Upload File :
Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/popup-maker/classes/Services/License.php

<?php
/**
 * License management service.
 *
 * @package   PopupMaker
 * @copyright Copyright (c) 2024, Code Atlantic LLC
 */

namespace PopupMaker\Services;

use PopupMaker\Base\Service;

defined( 'ABSPATH' ) || exit;

/**
 * Pro license management.
 *
 * NOTE: For wordpress.org admins: This is only used if:
 * - The user explicitly entered a Pro license key.
 *
 * @package PopupMaker
 */
class License extends Service {

	/**
	 * EDD API URL.
	 *
	 * @var string
	 */
	const API_URL = 'https://wppopupmaker.com/edd-sl-api/';

	/**
	 * Item ID.
	 *
	 * @var int
	 */
	const ID = 480187;

	/**
	 * Option key.
	 *
	 * @var string
	 */
	const OPTION_KEY = 'popup_maker_license';

	/**
	 * Settings key.
	 *
	 * @var string
	 */
	const SETTINGS_KEY = 'popup_maker_pro_license_key';

	/**
	 * Loss delay transient.
	 *
	 * @var string
	 */
	const DELAY_TRANSIENT = 'popup_maker_license_loss_delay';

	/**
	 * Loss check transient.
	 *
	 * @var string
	 */
	const CHECK_TRANSIENT = 'popup_maker_license_loss_check';

	/**
	 * License data
	 *
	 * @var array{key:string|null,status:array{success:bool,license:'invalid'|'valid',item_id:int|false,item_name:string,license_limit:int,site_count:int,expires:string,activations_left:int,checksum:string,payment_id:int,customer_name:string,customer_email:string,price_id:string|int,error?:'no_activations_left'|'license_not_activable'|'missing'|'invalid'|'expired'|'revoked'|'item_name_mismatch'|'site_inactive'|'no_activations_left'|string|null,error_message?:string}|null,auto_activation?:array{enabled:bool,first_activated:string|null,last_seen:string|null,key_missing_since:string|null,key_hash:string|null}}|null
	 */
	private $license_data;

	/**
	 * Initialize license management.
	 *
	 * @param \PopupMaker\Plugin\Core $container Plugin container.
	 */
	public function __construct( $container ) {
		parent::__construct( $container );

		$this->register_hooks();
	}

	/**
	 * Register hooks.
	 *
	 * @return void
	 */
	public function register_hooks() {
		add_action( 'init', [ $this, 'autoregister' ] );
		add_action( 'popup_maker_license_status_check', [ $this, 'refresh_license_status' ] );
		add_action( 'admin_init', [ $this, 'schedule_crons' ] );
		add_filter( 'pum_settings_editor_args', [ $this, 'filter_settings_editor_args' ] );
	}

	/**
	 * Autoregister license with simplified security approach.
	 *
	 * For auto-activated licenses:
	 * - Never stores the actual key in database (uses placeholder)
	 * - Simple detection and setup without complex monitoring
	 * - Self-healing: automatically corrects DB state if needed
	 * - Detects loss of auto-activation for debugging
	 *
	 * @return void
	 */
	public function autoregister() {
		$constant_key = defined( 'POPUP_MAKER_PRO_LICENSE' ) && '' !== POPUP_MAKER_PRO_LICENSE ? POPUP_MAKER_PRO_LICENSE : false;
		$license_data = $this->get_license_data();

		// Handle new auto-activation setup.
		if ( $constant_key ) {
			// Clear any existing delay transients when key is detected.
			$this->clear_transients();

			$this->ensure_auto_activation_setup( $constant_key );
		} else {
			$is_auto_activated = isset( $license_data['auto_activation']['enabled'] );
			$constant_lost     = ! empty( $license_data['auto_activation']['constant_lost_at'] );

			// If auto-activation is enabled and constant is not lost, handle loss.
			if ( $is_auto_activated && ! $constant_lost ) {
				$this->handle_auto_activation_loss_with_delay();
			}
		}
	}

	/**
	 * Clear transients.
	 *
	 * @return void
	 */
	private function clear_transients(): void {
		delete_transient( self::DELAY_TRANSIENT );
		delete_transient( self::CHECK_TRANSIENT );
	}

	/**
	 * Handle loss of auto-activation constant with transient-based delay.
	 *
	 * Uses simple transient system to delay loss detection by 5 minutes,
	 * preventing false positives during plugin updates and WordPress operations.
	 *
	 * @return void
	 */
	private function handle_auto_activation_loss_with_delay(): void {
		// If delay transient exists, we're still waiting - do nothing
		if ( get_transient( self::DELAY_TRANSIENT ) ) {
			return;
		}

		// If check transient doesn't exist, start the delay period (first detection)
		if ( ! get_transient( self::CHECK_TRANSIENT ) ) {
			set_transient( self::DELAY_TRANSIENT, true, 10 );
			set_transient( self::CHECK_TRANSIENT, true, 0 ); // Non-expiring transient
			return;
		}

		// Delay expired and we still don't have the constant - mark as lost
		$this->handle_auto_activation_loss();

		// Clean up the check transient
		delete_transient( self::CHECK_TRANSIENT );
	}

	/**
	 * Ensure auto-activation is properly set up with simplified approach.
	 *
	 * @param string $key The license key from constant.
	 *
	 * @return void
	 */
	private function ensure_auto_activation_setup( string $key ): void {
		$license_data = $this->get_license_data();
		$db_key       = isset( $license_data['key'] ) ? $license_data['key'] : '';

		// Self-healing: ensure DB has placeholder, not real key
		if ( '***AUTO***' !== $db_key || empty( $license_data['auto_activation']['enabled'] ) ) {
			$license_data = [
				'key'             => '***AUTO***',
				'status'          => $license_data['status'] ?? null, // Preserve existing status
				'auto_activation' => [
					'enabled' => true,
				],
			];

			$this->update_license_data( $license_data );

			// Activate using the constant key if not already active
			if ( ! $this->is_license_active() ) {
				try {
					$this->maybe_activate_license( $key );
				// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
				} catch ( \Exception $e ) {
					// Do nothing on activation failure.
				}
			}
		} elseif ( ! empty( $license_data['auto_activation']['constant_lost_at'] ) ) {
			// Constant was restored - clear the loss timestamp and grace period
			unset( $license_data['auto_activation']['constant_lost_at'] );
			$this->update_license_data( $license_data );

			// Clear delay transients since constant is back
			$this->clear_transients();

			// Log restoration for debugging
			\PopupMaker\logging()->info( 'Auto-activation constant POPUP_MAKER_PRO_LICENSE was restored at ' . gmdate( 'Y-m-d H:i:s' ) );
		}
	}

	/**
	 * Handle loss of auto-activation constant.
	 *
	 * Tracks when auto-activation is lost for debugging and monitoring.
	 * Updates the license data with loss information and clears transients.
	 * Also clears license status to reflect the loss in UI.
	 *
	 * @return void
	 */
	private function handle_auto_activation_loss(): void {
		$license_data = $this->get_license_data();

		// Mark as lost and clear license status so UI reflects the change
		$license_data['auto_activation']['constant_lost_at'] = gmdate( 'Y-m-d H:i:s' );

		// Mark as lost and clear license status so UI reflects the change
		$license_data['auto_activation']['enabled'] = false;

		// Clear the key and status so UI shows inactive.
		$license_data['key']    = '';
		$license_data['status'] = null; // Clear cached status so UI shows inactive

		// Update the license data.
		$this->update_license_data( $license_data );

		// Clear the check transient since we've now marked it as lost
		$this->clear_transients();

		$current_time = gmdate( 'Y-m-d H:i:s' );

		// Log for debugging
		\PopupMaker\logging()->warning( 'Auto-activation constant POPUP_MAKER_PRO_LICENSE was removed at ' . $current_time );
	}

	/**
	 * Schedule cron jobs.
	 *
	 * @return void
	 */
	public function schedule_crons() {
		if ( ! wp_next_scheduled( 'popup_maker_license_status_check' ) ) {
			wp_schedule_event( time(), 'weekly', 'popup_maker_license_status_check' );
		}
	}

	/**
	 * Get license data.
	 *
	 * @return array{key:string|null,status:array{success:bool,license:'invalid'|'valid',item_id:int|false,item_name:string,license_limit:int,site_count:int,expires:string,activations_left:int,checksum:string,payment_id:int,customer_name:string,customer_email:string,price_id:string|int,error?:'no_activations_left'|'license_not_activable'|'missing'|'invalid'|'expired'|'revoked'|'item_name_mismatch'|'site_inactive'|'no_activations_left'|string|null,error_message?:string}|null,auto_activation?:array{enabled:bool,constant_lost_at?:string|null}}
	 */
	public function get_license_data() {
		if ( ! isset( $this->license_data ) ) {
			$this->license_data = \get_option( self::OPTION_KEY, [
				'key'             => '',
				'status'          => null,
				'auto_activation' => null,
			] );
		}

		return $this->license_data;
	}

	/**
	 * Update license data.
	 *
	 * @param array{key:string|null,status:array{success:bool,license:'invalid'|'valid',item_id:int|false,item_name:string,license_limit:int,site_count:int,expires:string,activations_left:int,checksum:string,payment_id:int,customer_name:string,customer_email:string,price_id:string|int,error?:'no_activations_left'|'license_not_activable'|'missing'|'invalid'|'expired'|'revoked'|'item_name_mismatch'|'site_inactive'|'no_activations_left'|string|null,error_message?:string}|null,auto_activation?:array{enabled:bool,constant_lost_at?:string|null}} $license_data License data.
	 *
	 * @return bool
	 */
	private function update_license_data( array $license_data ): bool {
		if ( \update_option( self::OPTION_KEY, $license_data ) ) {
			$this->license_data = $license_data;

			return true;
		}

		return false;
	}

	/**
	 * Get license key for display purposes.
	 *
	 * For auto-activated licenses, this always returns the constant value
	 * or placeholder - never exposes real keys from database.
	 *
	 * @uses \PopupMaker\Services\License::get_license_data() For source of truth.
	 *
	 * @return string
	 */
	public function get_license_key(): string {
		$license_data = $this->get_license_data();

		// Handle auto-activated licenses securely
		if ( ! empty( $license_data['auto_activation']['enabled'] ) ) {
			// For auto-activated, always return constant or placeholder
			return defined( 'POPUP_MAKER_PRO_LICENSE' ) && ! empty( POPUP_MAKER_PRO_LICENSE )
				? POPUP_MAKER_PRO_LICENSE
				: $license_data['key']; // Will be '***AUTO***' placeholder
		}

		// Return regular stored key
		return ! empty( $license_data['key'] ) ? $license_data['key'] : '';
	}

	/**
	 * Get license key for EDD API calls (extensions, updater, etc.).
	 *
	 * Unlike get_license_key(), this never returns display placeholders.
	 *
	 * @return string
	 */
	public function get_api_license_key(): string {
		$key = $this->get_raw_license_key();

		return null !== $key ? $key : '';
	}

	/**
	 * Get raw license key for internal API calls only.
	 *
	 * This method should NEVER be used for display purposes.
	 * For auto-activated licenses, it always retrieves from the constant.
	 *
	 * @return string|null
	 */
	private function get_raw_license_key(): ?string {
		$license_data = $this->get_license_data();

		// For auto-activated licenses, always use the constant
		if ( ! empty( $license_data['auto_activation']['enabled'] ) ) {
			return defined( 'POPUP_MAKER_PRO_LICENSE' ) && ! empty( POPUP_MAKER_PRO_LICENSE ) ? POPUP_MAKER_PRO_LICENSE : null;
		}

		// For regular licenses, use stored key
		return ! empty( $license_data['key'] ) ? $license_data['key'] : null;
	}

	/**
	 * Get license status.
	 *
	 * @uses \PopupMaker\Services\License::get_license_data() For source of truth.
	 *
	 * @param bool $refresh Whether to refresh license status.
	 *
	 * @return array{success:bool,license:'invalid'|'valid',item_id:int|false,item_name:string,license_limit:int,site_count:int,expires:string,activations_left:int,checksum:string,payment_id:int,customer_name:string,customer_email:string,price_id:string|int,error?:'no_activations_left'|'license_not_activable'|'missing'|'invalid'|'expired'|'revoked'|'item_name_mismatch'|'site_inactive'|'no_activations_left'|string|null,error_message?:string}|null
	 */
	public function get_license_status_data( ?bool $refresh = false ) {
		if ( $refresh ) {
			$this->refresh_license_status();
		}

		$license_data = $this->get_license_data();

		$license_status = isset( $license_data['status'] ) ? wp_parse_args( $license_data['status'], [
			'success'          => false, // bool.
			'license'          => 'invalid', // valid or invalid.
			'item_id'          => self::ID, // int or false.
			'item_name'        => '', // string.
			'license_limit'    => 1, // int. 0 = unlimited.
			'site_count'       => 0,
			'expires'          => '', // date or 'lifetime'.
			'activations_left' => 0, // int or 'unlimited'.
			'checksum'         => '', // string.
			'payment_id'       => 0, // int.
			'customer_name'    => '', // string.
			'customer_email'   => '', // string.
			'price_id'         => 0, // string or int.
		] ) : null;

		return $license_status;
	}

	/**
	 * Get license status.
	 *
	 * @return 'empty'|'inactive'|'expired'|'error'|'valid'
	 */
	public function get_license_status(): string {
		$license_key = $this->get_license_key();
		$status_data = $this->get_license_status_data();

		if ( empty( $license_key ) || empty( $status_data ) ) {
			return 'empty';
		}

		$status = 'inactive';

		// activate_license 'invalid' on anything other than valid, so if there was an error capture it
		if ( false === $status_data['success'] ) {
			$error = isset( $status_data['error'] ) ? $status_data['error'] : $status;

			switch ( $error ) {
				case 'expired':
					$status = 'expired';
					break;
				case 'revoked':
				case 'missing':
				case 'invalid':
				case 'site_inactive':
				case 'item_name_mismatch':
				case 'no_activations_left':
				case 'license_not_activable':
				default:
					$status = 'error';
					break;
			}
		} elseif ( 'valid' === $status_data['license'] ) {
				$status = 'valid';
		} else {
			$status = 'deactivated';
		}

		return $status;
	}

	/**
	 * Get license level.
	 *
	 * Only used in pro version.
	 *
	 * @return int Integer representing license level.
	 */
	public function get_license_level(): int {
		$license_status = $this->get_license_status_data();

		if ( empty( $license_status ) ) {
			return -1;
		}

		$price_id = isset( $license_status['price_id'] ) ? $license_status['price_id'] : null;

		switch ( $price_id ) {
			default:
				return -1;

			case false:
			case 0:
				return 0;

			case 1:
			case 2:
			case 3:
			case 4:
				return absint( $price_id );
		}
	}

	/**
	 * Get license tier (pro or pro_plus).
	 *
	 * @return string 'pro' or 'pro_plus' based on license data.
	 */
	public function get_license_tier(): string {
		$license_status = $this->get_license_status_data();

		if ( empty( $license_status ) ) {
			return 'pro';
		}

		// Check if license_tier is explicitly set in the license data.
		if ( ! empty( $license_status['license_tier'] ) ) {
			return in_array( $license_status['license_tier'], [ 'pro', 'pro_plus' ], true )
				? $license_status['license_tier']
				: 'pro';
		}

		// Fallback to 'pro' if not specified.
		return 'pro';
	}

	/**
	 * Update license key.
	 *
	 * Side Effects:
	 * - Update License Data
	 * - Reset License Status
	 * - Fire action: popup_maker_license_key_changed
	 *
	 * @param string $key License key.
	 *
	 * @return bool
	 */
	private function update_license_key( string $key ): bool {
		$license_data = $this->get_license_data();

		$new_key = trim( $key );
		$old_key = isset( $license_data['key'] ) ? $license_data['key'] : '';

		if ( $old_key === $new_key ) {
			return false;
		}

		$updated = $this->update_license_data( [
			'key'    => $new_key,
			'status' => null,
		] );

		if ( $updated ) {
			\PUM_Utils_Options::update( self::SETTINGS_KEY, $new_key );

			/**
			 * Fires when license key is changed.
			 *
			 * @param string $key License key.
			 * @param string $old_key Old license key.
			 */
			\do_action( 'popup_maker_license_key_changed', $new_key, $old_key );
		}

		return $updated;
	}

	/**
	 * Update license status.
	 *
	 * @param array<string,mixed> $license_status License status data.
	 *
	 * @return bool
	 */
	private function update_license_status( array $license_status ): bool {
		$license_data = $this->get_license_data();

		$previous_status = isset( $license_data['status'] ) ? $license_data['status'] : [];

		if ( ! empty( $license_status['error'] ) ) {
			$license_status['error_message'] = $this->get_license_error_message( $license_status );
		} else {
			unset( $license_status['error_message'] );
			unset( $license_status['error'] );
		}

		$license_data['status'] = $license_status;

		if ( $this->update_license_data( $license_data ) ) {
			/**
			 * Fires when license status is updated.
			 *
			 * @param array $license_status License status data.
			 * @param array $previous_status Previous license status data.
			 */
			\do_action( 'popup_maker_license_status_updated', $license_status, $previous_status );

			return true;
		}

		return false;
	}

	/**
	 * Get license expiration from license status data.
	 *
	 * @param bool $as_datetime Whether to return as DateTime object.
	 *
	 * @return string|null|\DateTime
	 */
	public function get_license_expiration( ?bool $as_datetime = false ) {
		$status_data = $this->get_license_status_data();

		if ( empty( $status_data ) ) {
			return null;
		}

		$expiration = isset( $status_data['expires'] ) ? $status_data['expires'] : null;

		return $as_datetime ?
			\DateTime::createFromFormat( 'Y-m-d H:i:s', $expiration ) :
			$expiration;
	}

	/**
	 * Fetch license status from remote server.
	 * This is a blocking request.
	 *
	 * @return bool
	 */
	public function refresh_license_status(): bool {
		$key = $this->get_raw_license_key();

		if ( empty( $key ) ) {
			return false;
		}

		try {
			$status = $this->check_license_status();
		} catch ( \Exception $e ) {
			$status = null;
		}

		return $this->update_license_status( $status );
	}

	/**
	 * Call the API.
	 *
	 * @param string     $action The action to call.
	 * @param array|null $params The parameters to pass to the API.
	 *
	 * @return array|null
	 *
	 * @throws \Exception If there is an error.
	 */
	private function api_call( string $action, ?array $params = null ) {
		$key = $this->get_raw_license_key();

		if ( empty( $key ) ) {
			return null;
		}

		$api_params = [
			'edd_action'  => $action,
			'license'     => $key,
			'item_id'     => self::ID,
			'item_name'   => rawurlencode( 'Popup Maker Pro' ),
			'url'         => home_url(),
			'environment' => function_exists( 'wp_get_environment_type' ) ? wp_get_environment_type() : 'production',
		];

		// Call the custom API.
		$response = wp_remote_post(
			self::API_URL,
			array_merge( $params ?? [], [
				'timeout'   => 15,
				'sslverify' => ! in_array( wp_get_environment_type(), [ 'local', 'development' ], true ),
				'body'      => $api_params,
			] )
		);

		// Make sure the response came back okay.
		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
			if ( is_wp_error( $response ) ) {
				$message = $response->get_error_message();
			} else {
				$message = __( 'An error occurred, please try again.', 'popup-maker' );
			}

			throw new \Exception( esc_html( $message ) );
		}

		$license_status = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( empty( $license_status ) ) {
			return null;
		}

		// No structure validation Fix: Validate expected fields exist and types match

		return $license_status;
	}

	/**
	 * Get license status from remote server.
	 *
	 * Side Effects:
	 * - API Call: Check License Status
	 *
	 * @return array{success:bool,license:'invalid'|'valid',item_id:int|false,item_name:string,license_limit:int,site_count:int,expires:string,activations_left:int,checksum:string,payment_id:int,customer_name:string,customer_email:string,price_id:string|int,error?:'no_activations_left'|'license_not_activable'|'missing'|'invalid'|'expired'|'revoked'|'item_name_mismatch'|'site_inactive'|'no_activations_left'|string|null,error_message?:string}|null
	 *
	 * @throws \Exception If there is an error.
	 */
	private function check_license_status() {
		return $this->api_call( 'check_license' );
	}

	/**
	 * Activate license.
	 *
	 * Side Effects:
	 * - API Call: Check License Status
	 * - Update Pro Activation Date
	 * - Update License Status
	 *
	 * @return bool
	 *
	 * @throws \Exception If there is an error.
	 */
	public function activate_license(): bool {
		$license_status = $this->api_call( 'activate_license' );

		// Bail early if the license status is empty.
		if ( empty( $license_status ) ) {
			return false;
		}

		$this->update_license_status( $license_status );

		if ( $this->is_license_active() ) {
			if ( ! \get_option( 'popup_maker_pro_activation_date', false ) ) {
				\update_option( 'popup_maker_pro_activation_date', time() );
			}
		}

		/**
		 * Fires when license is activated.
		 *
		 * @param array $license_status License status data.
		 */
		\do_action( 'popup_maker_license_activated', $license_status );

		return $this->is_license_active();
	}

	/**
	 * Deactivate license.
	 *
	 * Side Effects:
	 * - API Call: Deactivate License
	 * - Update License Status
	 *
	 * @return bool
	 *
	 * @throws \Exception If there is an error.
	 */
	public function deactivate_license(): bool {
		$license_status = $this->api_call( 'deactivate_license' );

		$this->update_license_status( $license_status );

		if ( empty( $license_status ) ) {
			return false;
		}

		$succeeded = 'deactivated' === $license_status['license'];

		/**
		 * Fires when license is activated.
		 *
		 * @param array $license_status License status data.
		 * @param bool  $succeeded      Whether the license was deactivated successfully.
		 */
		\do_action( 'popup_maker_license_deactivated', $license_status, $succeeded );

		return $succeeded;
	}

	/**
	 * Convert license error to human readable message.
	 *
	 * @param array{success:bool,license:'invalid'|'valid',item_id:int|false,item_name:string,license_limit:int,site_count:int,expires:string,activations_left:int,checksum:string,payment_id:int,customer_name:string,customer_email:string,price_id:string|int,error?:'no_activations_left'|'license_not_activable'|'missing'|'invalid'|'expired'|'revoked'|'item_name_mismatch'|'site_inactive'|'no_activations_left'|string|null,error_message?:string}|null $license_status License status data.
	 *
	 * @return string
	 */
	public function get_license_error_message( ?array $license_status = null ): string {
		if ( empty( $license_status ) ) {
			$license_status = $this->get_license_status_data();
		}

		if ( empty( $license_status['error'] ) ) {
			return '';
		}

		switch ( $license_status['error'] ) {
			case 'expired':
				$message = sprintf(
				/* translators: the license key expiration date */
					__( 'Your license key expired on %s.', 'popup-maker' ),
					date_i18n( \get_option( 'date_format' ), strtotime( $license_status['expires'], time() ) )
				);
				break;

			case 'disabled':
			case 'revoked':
				$message = __( 'Your license key has been disabled.', 'popup-maker' );
				break;

			case 'missing':
				$message = __( 'Invalid license.', 'popup-maker' );
				break;

			case 'invalid':
			case 'site_inactive':
				$message = __( 'Your license is not active for this URL.', 'popup-maker' );
				break;

			case 'item_name_mismatch':
				$message = sprintf(
				/* translators: the plugin name */
					__( 'This appears to be an invalid license key for %s.', 'popup-maker' ),
					__( 'Popup Maker', 'popup-maker' )
				);
				break;

			case 'no_activations_left':
				$message = __( 'Your license key has reached its activation limit.', 'popup-maker' );
				break;

			default:
				$message = __( 'An error occurred, please try again.', 'popup-maker' );
				break;
		}

		return $message;
	}

	/**
	 * Remove license.
	 *
	 * Caution: Destructive, this will remove the license key and status.
	 *
	 * @return bool
	 */
	public function remove_license(): bool {
		try {
			// Might be an invalid but used key, so we want to deactivate it. without checking if active.
			$this->deactivate_license();

			if ( \delete_option( self::OPTION_KEY ) ) {
				// Delete the old license key option & status as well.
				\PUM_Utils_Options::delete( self::SETTINGS_KEY );
				delete_option( 'popup_maker_pro_license_active' );

				/**
				 * Fires when license is removed.
				 */
				\do_action( 'popup_maker_license_removed' );

				return true;
			}
		} catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
			// Do nothing.
		}

		return false;
	}

	/**
	 * Safely update license key.
	 *
	 * Side effect:
	 * - This will remove the license if the key is empty.
	 * - This will update the license key if its different from the current key.
	 * - Auto-activated licenses cannot be modified via this method.
	 *
	 * @param string $key License key.
	 *
	 * @return bool
	 */
	public function maybe_update_license_key( string $key ): bool {
		// Prevent modification of auto-activated licenses
		$license_data = $this->get_license_data();
		if ( ! empty( $license_data['auto_activation']['enabled'] ) ) {
			return false;
		}

		if ( empty( $key ) ) {
			$this->remove_license();
			return true;
		} else {
			// If the key is starred, get the original key.
			if ( strpos( $key, '*' ) !== false ) {
				$key = $this->get_license_key();
			}

			return $this->update_license_key( $key );
		}
	}

	/**
	 * Safely attempt to activate the license.
	 *
	 * Proper external handlers with error control.
	 *
	 * @param string $key License key.
	 *
	 * @return bool
	 */
	public function maybe_activate_license( ?string $key = null ): bool {
		try {
			// Update the license key separately to avoid race condition.
			if ( $key ) {
				$this->maybe_update_license_key( $key );
			}

			// Activate the license.
			return $this->activate_license();
		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Check if license is active.
	 *
	 * @return bool
	 */
	public function is_license_active() {
		return 'valid' === $this->get_license_status();
	}

	/**
	 * Check if license is auto-activated via POPUP_MAKER_PRO_LICENSE constant.
	 *
	 * Uses database flag for reliable detection even when constant is removed.
	 *
	 * @return bool
	 */
	public function is_auto_activated(): bool {
		$license_data = $this->get_license_data();
		return ! empty( $license_data['auto_activation']['enabled'] );
	}

	/**
	 * Get auto-activation info for debugging.
	 *
	 * @return array{enabled:bool,constant_present:bool,db_key:string,constant_lost_at:string|null,status:string}|null
	 */
	public function get_auto_activation_info(): ?array {
		$license_data = $this->get_license_data();

		if ( ! isset( $license_data['auto_activation']['enabled'] ) ) {
			return null;
		}

		$constant_key     = defined( 'POPUP_MAKER_PRO_LICENSE' ) && ! empty( POPUP_MAKER_PRO_LICENSE ) ? POPUP_MAKER_PRO_LICENSE : null;
		$constant_present = null !== $constant_key;
		$constant_lost_at = $license_data['auto_activation']['constant_lost_at'] ?? null;

		// Determine status
		$status = 'unknown';
		if ( $constant_present ) {
			$status = $constant_lost_at ? 'restored' : 'active';
		} elseif ( $constant_lost_at ) {
			$status = 'lost';
		} else {
			$status = 'missing';
		}

		return [
			'enabled'          => true,
			'constant_present' => $constant_present,
			'db_key'           => $license_data['key'] ?? '',
			'constant_lost_at' => $constant_lost_at,
			'status'           => $status,
		];
	}

	/**
	 * Generate connection information for Pro upgrade.
	 *
	 * This method creates the necessary connection data for upgrading to Pro
	 * when Pro is not already installed.
	 *
	 * @return array{url:string,back_url:string}|null Returns connection info or null if conditions not met.
	 */
	public function generate_connect_info(): ?array {
		// Only generate connect info if license is active and Pro is not installed.
		if ( ! $this->is_license_active() || $this->container->is_pro_installed() ) {
			return null;
		}

		$license_key = $this->get_license_key();
		if ( empty( $license_key ) ) {
			return null;
		}

		// Use the Connect service to generate connection info.
		$connect_service = $this->container->get( 'connect' );
		return $connect_service->get_connect_info( $license_key );
	}

	/**
	 * Validate license for Pro upgrade workflow.
	 *
	 * Determines if the current license state allows for Pro upgrade.
	 *
	 * @return array{valid:bool,can_upgrade:bool,reason:string}
	 */
	public function validate_for_upgrade(): array {
		$license_key      = $this->get_license_key();
		$license_status   = $this->get_license_status();
		$is_pro_installed = $this->container->is_pro_installed();

		// No license key provided.
		if ( empty( $license_key ) ) {
			return [
				'valid'       => false,
				'can_upgrade' => false,
				'reason'      => 'no_license_key',
			];
		}

		// License is not active.
		if ( 'valid' !== $license_status ) {
			return [
				'valid'       => false,
				'can_upgrade' => false,
				'reason'      => "license_{$license_status}",
			];
		}

		// Pro is already installed.
		if ( $is_pro_installed ) {
			return [
				'valid'       => true,
				'can_upgrade' => false,
				'reason'      => 'pro_already_installed',
			];
		}

		// License is valid and Pro is not installed - can upgrade.
		return [
			'valid'       => true,
			'can_upgrade' => true,
			'reason'      => 'ready_for_upgrade',
		];
	}

	/**
	 * Filter settings editor args.
	 *
	 * Add the license key value to the settings editor args, since its not actually stored in options array.
	 *
	 * @param mixed $args Settings editor args.
	 * @return mixed Settings editor args.
	 */
	public function filter_settings_editor_args( $args ) {
		$value = isset( $args['current_values'][ self::SETTINGS_KEY ] ) ? $args['current_values'][ self::SETTINGS_KEY ] : '';

		try {
			$license_service = \PopupMaker\plugin( 'license' );
			$license_status  = $license_service->get_license_status_data();

			// Get the comprehensive license status from service
			$actual_status = $license_service->get_license_status();

			// Get the actual license key (either from form input or service)
			// $license_key = ! empty( $value ) ? trim( $value ) : $license_service->get_license_key();
			$license_key = $license_service->get_license_key();

			$license_key = self::star_key( $license_key );

			// Use the mapping function to get proper status and classes
			$status_mapping = $this->map_license_status( $actual_status );

			// Get the license tier (pro or pro_plus).
			$license_tier = $license_service->get_license_tier();

			// Get Pro installation status for template conditionals.
			$is_pro_installed = $this->container->is_pro_installed();
			$is_pro_active    = $this->container->is_pro_active();
			$pro_version      = $this->container->get_pro_version();

			// Check for auto-activation status messages
			$messages = ! empty( $license_status['error_message'] ) ? [ $license_status['error_message'] ] : [];

			// Add auto-activation disconnect message if applicable
			$auto_info = $license_service->get_auto_activation_info();
			if ( ! empty( $auto_info['constant_lost_at'] ) ) {
				$lost_date  = $auto_info['constant_lost_at'];
				$messages[] = sprintf(
					/* translators: the date and time of the auto-activation disconnect */
					__( 'Auto-activation was disconnected on %s. The POPUP_MAKER_PRO_LICENSE constant was removed from your configuration.', 'popup-maker' ),
					date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $lost_date ) )
				);
			}

			// Pass anything we want to the template here.
			$args['current_values'][ self::SETTINGS_KEY ] = [
				'key'              => $license_key,
				'status'           => $status_mapping['status'],
				'messages'         => $messages,
				'expires'          => $license_service->get_license_expiration(),
				'classes'          => $status_mapping['classes'],
				'license_tier'     => $license_tier,
				'is_pro_installed' => $is_pro_installed,
				'is_pro_active'    => $is_pro_active,
				'pro_version'      => $pro_version,
			];
		} catch ( \Exception $e ) {
			$args['current_values'][ self::SETTINGS_KEY ] = [
				'key'              => $this->star_key( trim( $value ) ),
				'status'           => 'invalid',
				/* translators: %s is the error message */
				'messages'         => [ sprintf( __( 'Error loading license status: %s', 'popup-maker' ), $e->getMessage() ) ],
				'expires'          => '',
				'classes'          => 'pum-license-invalid',
				'license_tier'     => 'pro', // Default to pro on error.
				'is_pro_installed' => $this->container->is_pro_installed(),
				'is_pro_active'    => $this->container->is_pro_active(),
				'pro_version'      => $this->container->get_pro_version(),
			];
		}

		return $args;
	}

	/**
	 * Map license status to template data.
	 *
	 * @param string $status License status from service.
	 * @return array Template data for license status.
	 */
	public function map_license_status( string $status ): array {
		switch ( $status ) {
			case 'valid':
				return [
					'status'  => 'valid',
					'classes' => 'pum-license-valid',
				];
			case 'deactivated':
				return [
					'status'  => 'deactivated',
					'classes' => 'pum-license-deactivated',
				];
			case 'expired':
				return [
					'status'  => 'expired',
					'classes' => 'pum-license-expired',
				];
			case 'empty':
				return [
					'status'  => 'empty',
					'classes' => 'pum-license-empty',
				];
			case 'error':
			default:
				return [
					'status'  => 'invalid',
					'classes' => 'pum-license-invalid',
				];
		}
	}

		/**
		 * Star the key.
		 *
		 * @param string $key The key to star.
		 *
		 * @return string The starred key.
		 */
	public static function star_key( string $key ): string {
		if ( empty( $key ) ) {
			return '';
		}

		return substr( $key, 0, 3 ) . str_repeat( '*', max( 0, strlen( $key ) - 6 ) ) . substr( $key, -3 );
	}
}

Home - Capital GMC Buick Regina

No data

Welcome to Capital GMC BUICK – REGINA

Thank you for choosing Capital GMC Buick | Regina, your premier certified Buick and GMC dealership proudly serving drivers in Regina and the surrounding communities. Whether you’re searching for a brand-new Buick or GMC vehicle or a meticulously inspected pre-owned model, we have a diverse selection to match your needs and lifestyle.

Beyond our impressive inventory, we offer a seamless and stress-free financing experience through our well-connected finance centre, where our team of experts is dedicated to securing the best loan or lease options for you, quickly, transparently, and hassle-free.

But our commitment to you doesn’t stop at the sale. Our state-of-the-art service centre is staffed with skilled Buick and GMC technicians who use the latest equipment and genuine OEM parts to keep your vehicle running at its best. From routine maintenance to complex repairs, we’ve got you covered.

Experience top-tier customer service, quality vehicles, and expert care, all in one place. Visit Capital GMC Buick | Regina today or call us at 306-205-8072 with any questions. We’re here to help!

Ask a Question