cacheDuration = defined('ADBLOCKGUARD_DEBUG') && ADBLOCKGUARD_DEBUG ? HOUR_IN_SECONDS : DAY_IN_SECONDS;
if ($licenseKey) {
$this->licenseKey = $licenseKey;
update_option('wuadblockguard_license_key', $licenseKey);
} else {
$this->licenseKey = get_option('wuadblockguard_license_key', '');
}
add_action('plugins_loaded', [$this, 'registerPluginRowMeta']);
$this->initializeUpdater();
$this->setupUpdateTransientsClear();
}
public static function getInstance($licenseKey = null)
{
if (self::$instance === null) {
self::$instance = new self($licenseKey);
}
return self::$instance;
}
public function getProductId()
{
return $this->productId;
}
public function getStoreUrl()
{
return $this->storeUrl;
}
public function getLicenseKey()
{
return $this->licenseKey;
}
public function setLicenseKey($licenseKey)
{
$this->licenseKey = $licenseKey;
update_option('wuadblockguard_license_key', $licenseKey);
}
public function activateLicense()
{
$api_params = array(
'edd_action' => 'activate_license',
'license' => $this->licenseKey,
'item_id' => $this->getProductId(),
'url' => home_url(),
);
$response = wp_remote_post(
$this->getLicenseApiUrl(),
array(
'body' => $api_params,
'timeout' => 20,
'headers' => array(
'User-Agent' => 'AdBlockGuard/' . ADBLOCKGUARD_VERSION . '; ' . home_url(),
'Accept' => 'application/json',
'X-Wutime-License' => '1',
),
)
);
if (is_wp_error($response)) {
return array('success' => false, 'error' => $response->get_error_message());
}
$license_data = json_decode(wp_remote_retrieve_body($response));
if ($license_data && $license_data->license === 'valid') {
$this->updateLicenseStatus('valid', $license_data->expires ?? null);
PluginLogger::log('info', 'License activated successfully', [
'license' => substr($this->licenseKey, 0, 6) . '…',
'expires' => $license_data->expires ?? 'unknown',
'bypass' => true,
]);
return ['success' => true, 'expires' => $license_data->expires ?? null];
}
$this->updateLicenseStatus('invalid');
return array('success' => false, 'error' => $license_data->error ?? 'Activation failed');
}
private function getLicenseApiUrl(): string
{
return rtrim($this->getStoreUrl(), '/') . '/edd-sl/';
}
public function deactivateLicense()
{
$api_params = array(
'edd_action' => 'deactivate_license',
'license' => $this->licenseKey,
'item_id' => $this->getProductId(),
'url' => home_url(),
);
$response = wp_remote_post(
$this->getLicenseApiUrl(),
array(
'body' => $api_params,
'timeout' => 20,
'headers' => array(
'User-Agent' => 'AdBlockGuard/' . ADBLOCKGUARD_VERSION . '; ' . home_url(),
'Accept' => 'application/json',
'X-Wutime-License' => '1',
),
)
);
if (is_wp_error($response)) {
PluginLogger::log('debug', 'Deactivate License - WP Error: ' . $response->get_error_message());
return array('success' => false, 'error' => $response->get_error_message());
}
$license_data = json_decode(wp_remote_retrieve_body($response));
if ($license_data && ($license_data->license ?? '') === 'deactivated') {
PluginLogger::log('info', 'License deactivated', [
'license' => substr($this->licenseKey, 0, 6) . '…',
'bypass' => true,
]);
$this->clearLicenseData();
return ['success' => true];
}
PluginLogger::log('debug', 'Deactivate License - Full Response', [
'response' => $license_data,
]);
return array(
'success' => false,
'error' => $license_data->error ?? 'Deactivation failed',
);
}
public function registerPluginRowMeta()
{
if (has_filter('plugin_row_meta', [$this, 'pluginRowMeta']) === false) {
add_filter('plugin_row_meta', [$this, 'pluginRowMeta'], 10, 4);
}
if (has_filter('plugin_action_links_ad-block-guard/ad-block-guard.php', [$this, 'pluginActionLinks']) === false) {
add_filter('plugin_action_links_ad-block-guard/ad-block-guard.php', [$this, 'pluginActionLinks'], 10, 2);
}
}
public function pluginRowMeta($plugin_meta, $plugin_file, $plugin_data = [], $status = '')
{
if ($plugin_file !== ADBLOCKGUARD_PLUGIN_BASE_NAME) {
return $plugin_meta;
}
$store_base_url = rtrim(ADBLOCKGUARD_STORE_URL, '/');
$plugin_site_url = $plugin_data['PluginURI'] ?? ($store_base_url . '/downloads/wp-adblock-guard/');
$filtered_meta = [];
$has_view_details_link = false;
foreach ($plugin_meta as $meta_link) {
if (strpos($meta_link, 'open-plugin-details-modal') !== false || strpos($meta_link, 'View details') !== false) {
$has_view_details_link = true;
}
// Remove native "Visit plugin site"; we append it in custom order.
if (strpos($meta_link, 'Visit plugin site') !== false) {
continue;
}
$filtered_meta[] = $meta_link;
}
$plugin_meta = $filtered_meta;
if (!$has_view_details_link) {
$plugin_slug = $plugin_data['slug'] ?? dirname(ADBLOCKGUARD_PLUGIN_BASE_NAME);
/* translators: %s: Plugin name. */
$more_info_aria = sprintf(__('More information about %s', 'ad-block-guard'), ADBLOCKGUARD_NAME);
$plugin_meta[] = sprintf(
'%s',
esc_url(
network_admin_url(
'plugin-install.php?tab=plugin-information&plugin=' . rawurlencode($plugin_slug) . '&TB_iframe=true&width=600&height=550'
)
),
esc_attr($more_info_aria),
esc_attr(ADBLOCKGUARD_NAME),
esc_html__('View details', 'ad-block-guard')
);
}
/* translators: %s: Plugin name. */
$visit_site_aria = sprintf(__('Visit plugin site for %s', 'ad-block-guard'), ADBLOCKGUARD_NAME);
$plugin_meta[] = sprintf(
'%s',
esc_url($plugin_site_url),
esc_attr($visit_site_aria),
esc_html__('Visit plugin site', 'ad-block-guard')
);
$plugin_meta[] = '' . esc_html__('Support', 'ad-block-guard') . '';
$plugin_meta[] = '' . esc_html__('Review', 'ad-block-guard') . '';
return $plugin_meta;
}
public function pluginActionLinks($links, $plugin_file)
{
if ($plugin_file === 'ad-block-guard/ad-block-guard.php') {
$settings_link = '' . esc_html__('Settings', 'ad-block-guard') . '';
if ($this->isLicenseValid()) {
array_unshift($links, $settings_link);
$links[] = '' . esc_html__('Manage License', 'ad-block-guard') . '';
} else {
$upgrade_link = '' . esc_html__('Get AdBlock Pro', 'ad-block-guard') . '';
array_unshift($links, $settings_link);
array_unshift($links, $upgrade_link);
$links[] = '' . esc_html__('Activate License', 'ad-block-guard') . '';
}
}
return $links;
}
public function isLicenseValid()
{
$lastCheck = get_transient(ADBLOCKGUARD_LICENSE_LAST_CHECK);
if ($lastCheck) {
$licenseStatus = get_option('wuadblockguard_license_status', 'inactive');
return $licenseStatus === 'valid';
}
$result = $this->checkLicenseValidity();
return $result['success'] && $result['status'] === 'valid';
}
public function isUpdateAvailable()
{
// Plugin updates are provided by WordPress.org only.
return false;
}
public function initializeUpdater()
{
// Licensing remains active in this class, but plugin update metadata stays WordPress-native.
return;
}
public function setupUpdateTransientsClear()
{
add_action('upgrader_process_complete', function ($upgrader_object, $options) {
if ($options['action'] == 'update' && $options['type'] == 'plugin') {
if (isset($options['plugins']) && in_array(ADBLOCKGUARD_PLUGIN_BASE_NAME, $options['plugins'], true)) {
delete_transient('wuadblockguard_latest_version');
delete_transient('wuadblockguard_product_details');
delete_option('wuadblockguard_license_status');
delete_option('wuadblockguard_license_expires');
}
}
}, 10, 2);
}
public function updateProductDetails()
{
// No-op: keep method for backwards compatibility with scheduled hooks.
return;
}
public function checkLicense()
{
$this->checkLicenseValidity();
}
public function getProductDetails()
{
$transient_name = 'wuadblockguard_product_details';
$product_details = get_transient($transient_name);
if ($product_details === false) {
$product_details = [
'permalink' => trailingslashit($this->getStoreUrl()) . 'downloads/wp-adblock-guard/',
'title' => 'AdBlock Guard Pro',
'price' => '',
'version' => ADBLOCKGUARD_VERSION,
];
set_transient($transient_name, $product_details, WEEK_IN_SECONDS);
}
return $product_details;
}
public function checkLicenseValidity($forceCheck = false)
{
$lastCheck = get_transient(ADBLOCKGUARD_LICENSE_LAST_CHECK);
if (!$forceCheck && $lastCheck) {
$licenseStatus = get_option('wuadblockguard_license_status', 'inactive');
return [
'success' => $licenseStatus === 'valid',
'status' => $licenseStatus,
'expires' => get_option('wuadblockguard_license_expires', ''),
'cached' => true
];
}
$api_params = [
'edd_action' => 'check_license',
'license' => $this->getLicenseKey(),
'item_id' => $this->getProductId(),
'url' => home_url(),
];
$response = wp_remote_post(
$this->getLicenseApiUrl(),
[
'body' => $api_params,
'timeout' => 20,
'headers' => [
'User-Agent' => 'AdBlockGuard/' . ADBLOCKGUARD_VERSION . '; ' . home_url(),
'Accept' => 'application/json',
'X-Wutime-License' => '1',
],
]
);
if (is_wp_error($response)) {
PluginLogger::log(
'error',
'Check License Validity - WP Error: ' . $response->get_error_message()
);
return [
'success' => false,
'status' => 'error',
'error' => $response->get_error_message()
];
}
$body = wp_remote_retrieve_body($response);
// Bail early if response is empty or clearly not JSON
if (!is_string($body) || $body === '' || $body[0] !== '{') {
$this->logHttpDebug('check_license (non-json)', $response);
PluginLogger::log(
'error',
'License Check Error: Non-JSON response from license server.'
);
return [
'success' => false,
'status' => 'error',
'error' => esc_html__('Invalid response from license server.', 'ad-block-guard')
];
}
$license_data = json_decode($body);
if ($license_data && isset($license_data->license)) {
$this->updateLicenseStatus(
$license_data->license,
$license_data->expires ?? null
);
return [
'success' => $license_data->license === 'valid',
'status' => $license_data->license,
'expires' => $license_data->expires ?? '',
'cached' => false
];
}
// JSON decoded but still not what EDD should return
$this->logHttpDebug('check_license (invalid-json)', $response);
PluginLogger::log(
'error',
'License Check Error: Invalid response from license server.'
);
return [
'success' => false,
'status' => 'error',
'error' => esc_html__('Invalid response from license server.', 'ad-block-guard')
];
}
private function logHttpDebug($context, $response)
{
// Guard: handle unexpected input
if (!is_array($response)) {
PluginLogger::log('error', $context . ' HTTP debug (invalid response type)', [
'response_type' => gettype($response),
]);
return;
}
$code = wp_remote_retrieve_response_code($response);
$ctype = wp_remote_retrieve_header($response, 'content-type');
$server = wp_remote_retrieve_header($response, 'server');
// Cloudflare correlation headers (gold for searching CF events)
$cfRay = wp_remote_retrieve_header($response, 'cf-ray');
$cfCache = wp_remote_retrieve_header($response, 'cf-cache-status');
$cfRequestId = wp_remote_retrieve_header($response, 'cf-request-id'); // sometimes present
// Other useful HTTP headers
$date = wp_remote_retrieve_header($response, 'date');
$cacheCtrl = wp_remote_retrieve_header($response, 'cache-control');
$expires = wp_remote_retrieve_header($response, 'expires');
$via = wp_remote_retrieve_header($response, 'via');
$xCache = wp_remote_retrieve_header($response, 'x-cache');
$xServedBy = wp_remote_retrieve_header($response, 'x-served-by');
$xFrame = wp_remote_retrieve_header($response, 'x-frame-options');
$body = wp_remote_retrieve_body($response);
$bodyStr = is_string($body) ? $body : '';
// Keep logs safe + useful: short preview + detect common block/challenge pages
$bodyHead = $bodyStr !== '' ? substr($bodyStr, 0, 600) : '';
$isHtml = $bodyStr !== '' && stripos($bodyStr, 'Just a moment') !== false;
// Pull request info that helps you correlate "what triggered this"
$requestUri = isset($_SERVER['REQUEST_URI'])
? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI']))
: 'N/A';
$siteUrl = home_url();
$storeUrl = method_exists($this, 'getStoreUrl') ? $this->getStoreUrl() : (defined('ADBLOCKGUARD_STORE_URL') ? ADBLOCKGUARD_STORE_URL : 'N/A');
// Optional: log the outgoing UA if you set one in wp_remote_post args
// (won't always be available, but harmless to include when you provide it)
$outgoingUa = 'N/A';
if (isset($response['http_response']) && is_object($response['http_response'])) {
// Not guaranteed; WP HTTP API doesn't reliably expose the request UA.
$outgoingUa = 'unknown';
}
PluginLogger::log('error', $context . ' HTTP debug', [
// Where we were when the error happened (customer site)
'request_uri' => $requestUri,
'site_url' => $siteUrl,
// Where we were calling (your license server)
'store_url' => $storeUrl,
// Core response signals
'code' => $code,
'content_type' => $ctype,
'server' => $server,
// Cloudflare correlation (paste cf_ray into CF Security Events search)
'cf_ray' => $cfRay,
'cf_cache' => $cfCache,
'cf_request_id'=> $cfRequestId,
// Useful caching / proxy headers
'date' => $date,
'cache_control'=> $cacheCtrl,
'expires' => $expires,
'via' => $via,
'x_cache' => $xCache,
'x_served_by' => $xServedBy,
'x_frame' => $xFrame,
// Body summary (don’t dump full body)
'body_len' => $bodyStr !== '' ? strlen($bodyStr) : 0,
'body_head' => $bodyHead,
// Simple classifiers to speed up triage
'flags' => [
'is_html' => $isHtml,
'looks_like_cf_challenge' => $hasCfMoment,
],
]);
}
public function validateLicenseCron()
{
$this->checkLicenseValidity(true);
}
private function updateLicenseStatus($status, $expires = null)
{
update_option('wuadblockguard_license_status', $status);
set_transient(ADBLOCKGUARD_LICENSE_LAST_CHECK, time() + $this->cacheDuration, $this->cacheDuration);
if ($expires) {
update_option('wuadblockguard_license_expires', $expires);
}
}
private function clearLicenseData()
{
delete_transient(ADBLOCKGUARD_LICENSE_LAST_CHECK);
delete_option('wuadblockguard_license_key');
delete_option('wuadblockguard_license_status');
delete_option('wuadblockguard_license_expires');
}
public function getUpgradeLink()
{
$productDetails = $this->getProductDetails();
return $productDetails['permalink'] . '?utm_source=WordPress&utm_medium=all-plugins&utm_campaign=liteplugin&utm_locale=' . get_locale() . '&utm_content=' . rawurlencode(esc_html__('Get !AdBlock Guard Pro', 'ad-block-guard'));
}
}