AlkantarClanX12
Your IP : 216.73.217.24
<?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
Skip to content
{{ $t(category) }}
Error
{{vehicle.modelData.year}} {{vehicle.modelData.make}} {{vehicle.modelData.model}}
Starting from {{vehicle.modelData.startingPrice | moneyFormat(lang)}}
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
Capital GMC Buick – Regina
Contact Us
Have a question or need assistance? Fill out the form and we will reach out to you as soon as possible.
Notice: JavaScript is required for this content.
CLOSE
Schedule a Visit
Let us know when you are coming and how we can assist you. We can ensure someone will be on hand to help you out at the desired date and time.
Notice: JavaScript is required for this content.
CLOSE
Find a Career
Have a look at our list of available positions and apply online today to join our team!
×
Opportunities to Grow
The auto industry is constantly changing and we want to continue to grow. We offer growth, leadership & mentorship programs to allow our staff to grow with us.
×
Competitive Salary
We have a significant earning potential with incentive-based pay in most roles. We also offer an employee referral bonus with paid bonuses.
×
Health & Dental
We offer a comprehensive benefits package including extended health, dental, and vision care. We also include paramedical, life insurance, paid sick leave, short & long-term disability coverages.
×
Vacation
We value our employees and want everyone to take their vacation time. We offer a minimum of 2 weeks vacation each year.
×
Training & Development
We have many opportunities for paid education and training in-house as well as training from the Manufacturer.
×
$10,000 Cash Giveaway – Terms & Conditions
All October long, stop by Capital GMC Buick Cadillac, to enter for your chance to win $10,000 cash. No purchase is required, but entries must be made in-store.
The contest is open to residents of Saskatchewan who are 18+. Dealership employees and their households are not eligible. Entries will be accepted from October 1 to October 31, 2025. A random draw will take place on November 1, 2025.
The prize is one $10,000 award, paid by cheque, and must be accepted as awarded. Winner will be contacted by phone or email and must respond within 7 days or another entry may be drawn. Odds of winning depend on the number of entries received.By entering, you agree that Capital Automotive Group may use your name and photo for winner announcements. The contest is governed by the laws of Saskatchewan.
Notice: JavaScript is required for this content.
CLOSE