- WordPress core y plugins - Tema Twenty Twenty-Four configurado - Plugin allow-unfiltered-html.php simplificado - .gitignore configurado para excluir wp-config.php y uploads 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1763 lines
53 KiB
PHP
Executable File
1763 lines
53 KiB
PHP
Executable File
<?php // phpcs:ignoreFile
|
||
|
||
use AdvancedAds\Utilities\Conditional;
|
||
use AdvancedAds\Framework\Utilities\Params;
|
||
|
||
/**
|
||
* AdSense Management API class.
|
||
*/
|
||
class Advanced_Ads_AdSense_MAPI {
|
||
|
||
/**
|
||
* The Adsense MAPI settings in the database.
|
||
*
|
||
* @var string
|
||
*/
|
||
const OPTION_KEY = 'advanced-ads-adsense-mapi';
|
||
|
||
/**
|
||
* The URL for retrieving alerts from the Google AdSense API.
|
||
*
|
||
* @var string
|
||
*/
|
||
const ALERTS_URL = 'https://adsense.googleapis.com/v2/accounts/PUBID/alerts';
|
||
|
||
/**
|
||
* Google Adsense Client ID.
|
||
*
|
||
* @var string
|
||
*/
|
||
const CID = '400595147946-3ot506jh20qld7bqmg1l87ms4vn2uok5.apps.googleusercontent.com';
|
||
|
||
/**
|
||
* Google AdSense Client Secret.
|
||
*
|
||
* @var string
|
||
*/
|
||
const CS = 'WKX8ghwUbxdrBcVmZ9WXOKph';
|
||
|
||
/**
|
||
* The redirect URI for the Google AdSense API authentication.
|
||
*
|
||
* @var string
|
||
*/
|
||
const REDIRECT_URI = 'https://c.wpadvancedads.com/oauth.php';
|
||
|
||
/**
|
||
* The maximum number of API calls allowed per 24 hours.
|
||
*
|
||
* @var int
|
||
*/
|
||
const CALL_PER_24H = 20;
|
||
|
||
/**
|
||
* Class Mapi
|
||
*
|
||
* @var Advanced_Ads_AdSense_MAPI
|
||
*/
|
||
private static $instance = null;
|
||
|
||
/**
|
||
* Default options.
|
||
*
|
||
* @var array
|
||
*/
|
||
private static $default_options = [];
|
||
|
||
/**
|
||
* Default account options.
|
||
*
|
||
* @var array
|
||
*/
|
||
private static $empty_account_data = [
|
||
'default_app' => [
|
||
'access_token' => '',
|
||
'refresh_token' => '',
|
||
'expires' => 0,
|
||
'token_type' => '',
|
||
],
|
||
'user_app' => [
|
||
'access_token' => '',
|
||
'refresh_token' => '',
|
||
'expires' => 0,
|
||
'token_type' => '',
|
||
],
|
||
'ad_units' => [],
|
||
'details' => [],
|
||
'alerts' => [],
|
||
];
|
||
|
||
/**
|
||
* Instance constructor
|
||
*/
|
||
private function __construct() {
|
||
|
||
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
|
||
|
||
add_action( 'wp_ajax_advads_gadsense_mapi_confirm_code', [ $this, 'ajax_confirm_code' ] );
|
||
add_action( 'wp_ajax_advads_gadsense_mapi_get_details', [ $this, 'ajax_get_account_details' ] );
|
||
add_action( 'wp_ajax_advads_gadsense_mapi_select_account', [ $this, 'ajax_account_selected' ] );
|
||
add_action( 'wp_ajax_advads_mapi_get_adCode', [ $this, 'ajax_get_ad_code' ] );
|
||
add_action( 'wp_ajax_advads-mapi-reconstructed-code', [ $this, 'ajax_save_reconstructed_code' ] );
|
||
add_action( 'wp_ajax_advads-mapi-save-manual-code', [ $this, 'ajax_save_manual_code' ] );
|
||
add_action( 'wp_ajax_advads-mapi-revoke-token', [ $this, 'ajax_revoke_tokken' ] );
|
||
add_action( 'wp_ajax_advads-mapi-get-alerts', [ $this, 'ajax_get_account_alerts' ] );
|
||
add_action( 'wp_ajax_advads-mapi-dismiss-alert', [ $this, 'ajax_dismiss_alert' ] );
|
||
add_action( 'wp_ajax_advads_adsense_report_refresh', [ 'Advanced_Ads_Overview_Widgets_Callbacks', 'ajax_gadsense_dashboard' ] );
|
||
|
||
add_action( 'admin_footer', [ $this, 'admin_footer' ] );
|
||
|
||
self::$default_options = [
|
||
'accounts' => [],
|
||
'ad_codes' => [],
|
||
'unsupported_units' => [],
|
||
'quota' => [
|
||
'count' => self::CALL_PER_24H,
|
||
'ts' => 0,
|
||
],
|
||
'connect_error' => [],
|
||
];
|
||
|
||
add_action( 'wp_loaded', [ $this, 'update_ad_health_notices' ] );
|
||
add_filter( 'advanced-ads-support-messages', [ 'Advanced_Ads_AdSense_MAPI', 'adsense_warnings_check' ] );
|
||
}
|
||
|
||
/**
|
||
* Update all MAPI related notices.
|
||
*/
|
||
public function update_ad_health_notices() {
|
||
$mapi_options = self::get_option();
|
||
|
||
$connection_error_messages = self::get_connect_error_messages();
|
||
|
||
$health_class = Advanced_Ads_Ad_Health_Notices::get_instance();
|
||
|
||
// Last connection failed.
|
||
if ( isset( $mapi_options['connect_error'] ) && ! empty( $mapi_options['connect_error'] ) ) {
|
||
|
||
if ( isset( $connection_error_messages[ $mapi_options['connect_error']['reason'] ] ) ) {
|
||
$health_class->add( 'adsense_connect_' . $mapi_options['connect_error']['reason'] );
|
||
} else {
|
||
$health_class->add(
|
||
'adsense_connect_' . $mapi_options['connect_error']['reason'],
|
||
[
|
||
'text' => esc_html__( 'Last AdSense account connection attempt failed.', 'advanced-ads' ) . ' ' . $mapi_options['connect_error']['message'],
|
||
'type' => 'problem',
|
||
]
|
||
);
|
||
}
|
||
|
||
foreach ( $health_class->notices as $key => $value ) {
|
||
// There was already a connection error but the user tried again and obtained another error.
|
||
if ( false !== stripos( $key, 'adsense_connect_' ) && 'adsense_connect_' . $mapi_options['connect_error']['reason'] !== $key ) {
|
||
$health_class->remove( $key );
|
||
}
|
||
}
|
||
} else {
|
||
|
||
// Once a connection has been established (or a the warning has been discarded on the AA settings page), remove connection related notices.
|
||
foreach ( $health_class->notices as $key => $value ) {
|
||
if ( false !== stripos( $key, 'adsense_connect_' ) ) {
|
||
$health_class->remove( $key );
|
||
}
|
||
}
|
||
}
|
||
|
||
$gadsense_data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$adsense_id = $gadsense_data->get_adsense_id();
|
||
$alerts = self::get_stored_account_alerts( $adsense_id );
|
||
|
||
// AdSense account alerts (can not happens simultaneously with the connection error).
|
||
if ( is_array( $alerts ) && isset( $alerts['items'] ) && is_array( $alerts['items'] ) && $alerts['items'] ) {
|
||
$item_ids = [];
|
||
$alerts_advads_messages = Advanced_Ads_Adsense_MAPI::get_adsense_alert_messages();
|
||
|
||
foreach ( $alerts['items'] as $internal_id => $item ) {
|
||
$item_id = isset( $item['id'] ) ? $item['id'] : str_replace( '-', '_', strtoupper( $item['type'] ) );
|
||
$item_ids[] = $item_id;
|
||
if ( isset( $alerts_advads_messages[ $item_id ] ) ) {
|
||
$health_class->add( 'adsense_alert_' . $item_id );
|
||
} else {
|
||
$health_class->add(
|
||
'adsense_alert_' . $item_id,
|
||
[
|
||
'text' => $item['message'] . ' ' . self::get_adsense_error_link( $item_id ),
|
||
'type' => 'problem',
|
||
]
|
||
);
|
||
}
|
||
}
|
||
|
||
// Remove notices that no more exist in the AdSense account (or have been dismissed).
|
||
$_remove_ids = [];
|
||
foreach ( $health_class->notices as $key => $value ) {
|
||
if ( false !== stripos( $key, 'adsense_alert_' ) ) {
|
||
$alert_id = substr( $key, strlen( 'adsense_alert_' ) );
|
||
if ( ! in_array( $alert_id, $item_ids, true ) ) {
|
||
$_remove_ids[] = $key;
|
||
}
|
||
}
|
||
}
|
||
foreach ( $_remove_ids as $id ) {
|
||
$health_class->remove( $id );
|
||
}
|
||
} else {
|
||
// No more alerts.
|
||
foreach ( $health_class->notices as $key => $value ) {
|
||
if ( false !== stripos( $key, 'adsense_alert_' ) ) {
|
||
$health_class->remove( $key );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get available quota and eventual message about remaining call
|
||
*/
|
||
public function get_quota() {
|
||
// Early bail!!
|
||
if ( self::use_user_app() ) {
|
||
return [ 'count' => PHP_INT_MAX ];
|
||
}
|
||
|
||
$now = time();
|
||
$options = $this->get_option();
|
||
if ( $now > $options['quota']['ts'] + ( 24 * 3600 ) ) {
|
||
return [
|
||
'count' => self::CALL_PER_24H,
|
||
];
|
||
}
|
||
|
||
return [
|
||
'count' => $options['quota']['count'],
|
||
'msg' => $this->get_quota_msg(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Get the readable quota
|
||
*/
|
||
public function get_quota_msg() {
|
||
|
||
$options = $this->get_option();
|
||
$now = time();
|
||
$secs = $options['quota']['ts'] + ( 24 * 3600 ) - $now;
|
||
$hours = floor( $secs / 3600 );
|
||
$mins = ceil( ( $secs - ( $hours * 3600 ) ) / 60 );
|
||
|
||
if ( 60 === $mins ) {
|
||
++$hours;
|
||
$mins = 0;
|
||
}
|
||
|
||
if ( 0 === (int) $options['quota']['count'] ) {
|
||
$msg = sprintf(
|
||
'No API call left before %1$s %2$s.',
|
||
sprintf( '%s hours', $hours ),
|
||
sprintf( '%s minutes', $mins )
|
||
);
|
||
|
||
if ( 0 === $hours ) {
|
||
$msg = 'No API call left before.';
|
||
}
|
||
|
||
if ( 0 === $mins ) {
|
||
$msg = 'No API call left.';
|
||
}
|
||
} else {
|
||
$msg = sprintf(
|
||
'%1$s for the next %2$s %3$s',
|
||
sprintf( '%s API calls remaining', $options['quota']['count'] ),
|
||
sprintf( '%s hours', $hours ),
|
||
sprintf( '%s minutes', $mins )
|
||
);
|
||
|
||
if ( 0 === $hours ) {
|
||
$msg = sprintf( '%s API calls remaining.', $options['quota']['count'] );
|
||
}
|
||
|
||
if ( 0 === $mins ) {
|
||
$msg = sprintf(
|
||
'%1$s for the next %2$s',
|
||
sprintf( '%s API calls remaining', $options['quota']['count'] ),
|
||
sprintf( '%s hours', $hours )
|
||
);
|
||
}
|
||
}
|
||
return $msg;
|
||
}
|
||
|
||
/**
|
||
* Decrement quota by 1, and return message about remaining call
|
||
*/
|
||
public function decrement_quota() {
|
||
$options = $this->get_option();
|
||
if ( 0 < $options['quota']['count'] ) {
|
||
--$options['quota']['count'];
|
||
$now = time();
|
||
if ( $now > $options['quota']['ts'] + ( 24 * 3600 ) ) {
|
||
$options['quota']['ts'] = $now;
|
||
}
|
||
update_option( self::OPTION_KEY, $options );
|
||
return $this->get_quota_msg();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get ad code from Google fpr a given ad unit
|
||
*
|
||
* @param string $ad_unit the ad unit to get the ad code for.
|
||
*
|
||
* @return array response to send back to the browser.
|
||
*/
|
||
public function get_ad_code( $ad_unit ) {
|
||
$options = self::get_option();
|
||
$gadsense_data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$adsense_id = $gadsense_data->get_adsense_id();
|
||
$unit_id = explode( ':', $ad_unit )[1];
|
||
$url = 'https://adsense.googleapis.com/v2/accounts/' . $adsense_id . '/adclients/ca-' . $adsense_id . '/adunits/' . $unit_id . '/adcode';
|
||
$access_token = self::get_access_token( $adsense_id );
|
||
|
||
foreach ( Advanced_Ads_Network_Adsense::get_instance()->get_external_ad_units() as $unit ) {
|
||
if (
|
||
isset( $unit->raw )
|
||
&& in_array( $unit->raw['contentAdsSettings']['type'], [ 'ARTICLE', 'FEED', 'MATCHED_CONTENT' ], true )
|
||
&& $ad_unit === $unit->id
|
||
&& ! array_key_exists( $ad_unit, $options['ad_codes'] )
|
||
) {
|
||
$options['unsupported_units'][ $ad_unit ] = 1;
|
||
update_option( self::OPTION_KEY, $options );
|
||
|
||
return [
|
||
'status' => false,
|
||
'msg' => 'doesNotSupportAdUnitType',
|
||
];
|
||
}
|
||
}
|
||
|
||
if ( ! isset( $access_token['msg'] ) ) {
|
||
$headers = [ 'Authorization' => 'Bearer ' . $access_token ];
|
||
$response = wp_remote_get( $url, [ 'headers' => $headers ] );
|
||
self::log( 'Get ad code for ad Unit [' . $ad_unit . ']' );
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
return [
|
||
'status' => false,
|
||
/* translators: %s: ad unit ID. */
|
||
'msg' => sprintf( esc_html__( 'Error while retrieving ad code for "%s".', 'advanced-ads' ), $ad_unit ),
|
||
'raw' => $response->get_error_message(),
|
||
];
|
||
}
|
||
$ad_code = json_decode( $response['body'], true );
|
||
if ( null === $ad_code || ! isset( $ad_code['adCode'] ) ) {
|
||
if ( $ad_code['error'] &&
|
||
$ad_code['error']['errors'] &&
|
||
isset( $ad_code['error']['errors'][0]['reason'] ) &&
|
||
'doesNotSupportAdUnitType' === $ad_code['error']['errors'][0]['reason']
|
||
) {
|
||
if ( array_key_exists( $ad_unit, $options['ad_codes'] ) && array_key_exists( $ad_unit, $options['unsupported_units'] ) ) {
|
||
unset( $options['unsupported_units'][ $ad_unit ] );
|
||
} else {
|
||
$options['unsupported_units'][ $ad_unit ] = 1;
|
||
}
|
||
update_option( self::OPTION_KEY, $options );
|
||
|
||
return [
|
||
'status' => false,
|
||
'msg' => 'doesNotSupportAdUnitType',
|
||
];
|
||
}
|
||
|
||
return [
|
||
'status' => false,
|
||
/* translators: %s: ad unit ID. */
|
||
'msg' => sprintf( esc_html__( 'Invalid response while retrieving ad code for "%s".', 'advanced-ads' ), $ad_unit ),
|
||
'raw' => $response['body'],
|
||
];
|
||
}
|
||
$options['ad_codes'][ $ad_unit ] = $ad_code['adCode'];
|
||
if ( isset( $options['unsupported_units'][ $ad_unit ] ) ) {
|
||
unset( $options['unsupported_units'][ $ad_unit ] );
|
||
}
|
||
update_option( self::OPTION_KEY, $options );
|
||
|
||
return $ad_code['adCode'];
|
||
}
|
||
|
||
// return the original error info.
|
||
return $access_token;
|
||
}
|
||
|
||
/**
|
||
* Convert ad unit data to v1.4 format to match the current UI and logics
|
||
*
|
||
* @param array $ad_unit ad unit in MAPI v2 format.
|
||
*
|
||
* @return array the ad unit in MAPI v1.4 format.
|
||
*/
|
||
public static function convert_ad_unit_format( $ad_unit ) {
|
||
$chunks = explode( '/', $ad_unit['name'] );
|
||
|
||
return [
|
||
'name' => $ad_unit['displayName'],
|
||
'nameV2' => $ad_unit['name'],
|
||
'id' => $ad_unit['reportingDimensionId'],
|
||
'code' => $chunks[ count( $chunks ) - 1 ],
|
||
'status' => $ad_unit['state'],
|
||
'contentAdsSettings' => $ad_unit['contentAdsSettings'],
|
||
'reportingDimensionId' => $ad_unit['reportingDimensionId'],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Get/Update ad unit list for a given client
|
||
*
|
||
* @param string $account publisher ID.
|
||
*
|
||
* @return array
|
||
*/
|
||
public static function get_ad_units( $account ) {
|
||
$url = 'https://adsense.googleapis.com/v2/accounts/' . $account . '/adclients/ca-' . $account . '/adunits?pageSize=350';
|
||
$access_token = self::get_access_token( $account );
|
||
|
||
if ( isset( $access_token['msg'] ) ) {
|
||
// Return the token related message.
|
||
return $access_token;
|
||
}
|
||
|
||
$response = wp_remote_get( $url, [ 'headers' => [ 'Authorization' => 'Bearer ' . $access_token ] ] );
|
||
self::log( 'Get ad units list for ca-' . $account );
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
return [
|
||
'status' => false,
|
||
/* translators: %s is the publisher ID. */
|
||
'msg' => sprintf( esc_html__( 'Error while retrieving ad unit list for "%s".', 'advanced-ads' ), $account ),
|
||
'raw' => $response->get_error_message(),
|
||
];
|
||
}
|
||
|
||
if ( trim( $response['body'] ) === '{}' ) {
|
||
// Empty account.
|
||
return [
|
||
'status' => false,
|
||
'msg' => sprintf(
|
||
/* translators: %1$s is the AdSense publisher ID; %2$s a starting a tag to the AdSense ad unit list and %3$s the closing link. */
|
||
esc_html__( 'The account "%1$s" does not seem to have any ad units. Please create some %2$shere%3$s.', 'advanced-ads' ),
|
||
$account,
|
||
'<a href="https://www.google.com/adsense/new/u/0/' . $account . '/myads/units" target="_blank">',
|
||
'</a>'
|
||
),
|
||
'raw' => $response['body'],
|
||
];
|
||
}
|
||
|
||
$response_body = json_decode( trim( $response['body'] ), true );
|
||
|
||
if ( null === $response_body || ! isset( $response_body['adUnits'] ) ) {
|
||
/* translators: %s is the publisher ID. */
|
||
$error_message = sprintf( esc_html__( 'Invalid response while retrieving ad unit list for "%s".', 'advanced-ads' ), $account );
|
||
// check the response for errors and display them for better problem-solving.
|
||
if ( ! empty( $response_body['error']['errors'] ) ) {
|
||
foreach ( $response_body['error']['errors'] as $err ) {
|
||
$hint = self::get_adsense_error_hint( $err['reason'] );
|
||
if ( $hint ) {
|
||
$error_message .= '<p class="description">' . $hint . '</p>';
|
||
}
|
||
$error_message .= sprintf(
|
||
'<p class="description">%1$s %2$s<br>%3$s %4$s</p>',
|
||
_x( 'Reason:', 'Reason of the API call error', 'advanced-ads' ),
|
||
$err['reason'],
|
||
_x( 'Message:', 'Error message from Google', 'advanced-ads' ),
|
||
$err['message']
|
||
);
|
||
}
|
||
}
|
||
|
||
return [
|
||
'status' => false,
|
||
'msg' => $error_message,
|
||
'raw' => trim( $response['body'] ),
|
||
];
|
||
}
|
||
|
||
$options = self::get_option();
|
||
|
||
if ( ! isset( $response_body['nextPageToken'] ) ) {
|
||
// Results fit into a single page (of 350 items).
|
||
$new_ad_units = [];
|
||
|
||
foreach ( $response_body['adUnits'] as $item ) {
|
||
$item = self::convert_ad_unit_format( $item );
|
||
$new_ad_units[ $item['id'] ] = $item;
|
||
}
|
||
|
||
$options['accounts'][ $account ]['ad_units'] = $new_ad_units;
|
||
update_option( self::OPTION_KEY, $options );
|
||
|
||
return [ 'done' => true ];
|
||
}
|
||
|
||
// There are more than 350 items in the account.
|
||
$page_token = $response_body['nextPageToken'];
|
||
$new_ad_units = [];
|
||
|
||
foreach ( $response_body['adUnits'] as $item ) {
|
||
$item = self::convert_ad_unit_format( $item );
|
||
$new_ad_units[ $item['id'] ] = $item;
|
||
}
|
||
|
||
$page = 1;
|
||
// While there is a next page of results do . . .
|
||
while ( $page_token ) {
|
||
$access_token = self::get_access_token( $account );
|
||
|
||
if ( isset( $access_token['msg'] ) ) {
|
||
// return the original error info.
|
||
return $access_token;
|
||
}
|
||
|
||
$next_url = $url . '&pageToken=' . urlencode( $page_token );
|
||
$headers = [
|
||
'Authorization' => 'Bearer ' . $access_token,
|
||
];
|
||
$response = wp_remote_get( $next_url, [ 'headers' => $headers ] );
|
||
self::log( 'Get ad unit list for ca-' . $account . ' page ' . $page );
|
||
++$page;
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
return [
|
||
'status' => false,
|
||
/* translators: %s is the publisher ID. */
|
||
'msg' => sprintf( esc_html__( 'Error while retrieving ad unit list for "%s".', 'advanced-ads' ), $account ),
|
||
'raw' => $response->get_error_message(),
|
||
];
|
||
}
|
||
|
||
$response_body = json_decode( $response['body'], true );
|
||
// Update page token if there are ad units left.
|
||
$page_token = isset( $response_body['nextPageToken'] ) ? $response_body['nextPageToken'] : false;
|
||
// Add items from this page into the final result.
|
||
foreach ( $response_body['adUnits'] as $item ) {
|
||
$item = self::convert_ad_unit_format( $item );
|
||
$new_ad_units[ $item['id'] ] = $item;
|
||
}
|
||
}
|
||
|
||
$options['accounts'][ $account ]['ad_units'] = $new_ad_units;
|
||
update_option( self::OPTION_KEY, $options );
|
||
|
||
return [ 'done' => true ];
|
||
}
|
||
|
||
/**
|
||
* Get the appropriate access token (default one or from user's Google app). Update it if needed.
|
||
*
|
||
* @param string $account publisher ID.
|
||
*
|
||
* @return array|string the access token on success, error info (as array) if an error occurred.
|
||
*/
|
||
public static function get_access_token( $account ) {
|
||
$options = self::get_option();
|
||
if ( isset( $options['accounts'][ $account ] ) ) {
|
||
if ( self::use_user_app() ) {
|
||
if ( time() > $options['accounts'][ $account ]['user_app']['expires'] ) {
|
||
$new_tokens = self::renew_access_token( $account );
|
||
if ( $new_tokens['status'] ) {
|
||
return $new_tokens['access_token'];
|
||
} else {
|
||
return $new_tokens;
|
||
}
|
||
} else {
|
||
return $options['accounts'][ $account ]['user_app']['access_token'];
|
||
}
|
||
} else {
|
||
if ( time() > $options['accounts'][ $account ]['default_app']['expires'] ) {
|
||
$new_tokens = self::renew_access_token( $account );
|
||
if ( $new_tokens['status'] ) {
|
||
return $new_tokens['access_token'];
|
||
} else {
|
||
// return all error info [arr].
|
||
return $new_tokens;
|
||
}
|
||
} else {
|
||
return $options['accounts'][ $account ]['default_app']['access_token'];
|
||
}
|
||
}
|
||
} else {
|
||
// Account does not exist.
|
||
if ( ! empty( $options['accounts'] ) ) {
|
||
// There is another account connected.
|
||
return [
|
||
'status' => false,
|
||
'msg' => esc_html__( 'It seems that some changes have been made in the Advanced Ads settings. Please refresh this page.', 'advanced-ads' ),
|
||
'reload' => true,
|
||
];
|
||
} else {
|
||
// No account at all.
|
||
return [
|
||
'status' => false,
|
||
/* translators: %s account id */
|
||
'msg' => wp_kses( sprintf( __( 'Advanced Ads does not have access to your account (<code>%s</code>) anymore.', 'advanced-ads' ), $account ), [ 'code' => true ] ),
|
||
'reload' => true,
|
||
];
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Renew the current access token.
|
||
*
|
||
* @param array $account AdSense account ID.
|
||
*/
|
||
public static function renew_access_token( $account ) {
|
||
$cid = self::CID;
|
||
$cs = self::CS;
|
||
$options = self::get_option();
|
||
$access_token = $options['accounts'][ $account ]['default_app']['access_token'];
|
||
$refresh_token = $options['accounts'][ $account ]['default_app']['refresh_token'];
|
||
|
||
if ( self::use_user_app() ) {
|
||
$gadsense_data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$_options = $gadsense_data->get_options();
|
||
$cid = ADVANCED_ADS_MAPI_CID;
|
||
$cs = ADVANCED_ADS_MAPI_CIS;
|
||
$access_token = $options['accounts'][ $account ]['user_app']['access_token'];
|
||
$refresh_token = $options['accounts'][ $account ]['user_app']['refresh_token'];
|
||
}
|
||
|
||
$url = 'https://www.googleapis.com/oauth2/v4/token';
|
||
$args = [
|
||
'body' => [
|
||
'refresh_token' => $refresh_token,
|
||
'client_id' => $cid,
|
||
'client_secret' => $cs,
|
||
'grant_type' => 'refresh_token',
|
||
],
|
||
];
|
||
|
||
$response = wp_remote_post( $url, $args );
|
||
self::log( 'Refresh access token' );
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
return [
|
||
'status' => false,
|
||
/* translators: %s account id */
|
||
'msg' => sprintf( esc_html__( 'error while renewing access token for "%s"', 'advanced-ads' ), $account ),
|
||
'raw' => $response->get_error_message(),
|
||
];
|
||
} else {
|
||
$tokens = json_decode( $response['body'], true );
|
||
// checking for the $tokens is not enough. it can be empty.
|
||
// monitored this, when the access token is revoked from the outside
|
||
// this can happen, when the user connects from another domain.
|
||
if ( null !== $tokens && isset( $tokens['expires_in'] ) ) {
|
||
$expires = time() + absint( $tokens['expires_in'] );
|
||
if ( self::use_user_app() ) {
|
||
$options['accounts'][ $account ]['user_app']['access_token'] = $tokens['access_token'];
|
||
$options['accounts'][ $account ]['user_app']['expires'] = $expires;
|
||
} else {
|
||
$options['accounts'][ $account ]['default_app']['access_token'] = $tokens['access_token'];
|
||
$options['accounts'][ $account ]['default_app']['expires'] = $expires;
|
||
}
|
||
update_option( self::OPTION_KEY, $options );
|
||
return [
|
||
'status' => true,
|
||
'access_token' => $tokens['access_token'],
|
||
];
|
||
} else {
|
||
return [
|
||
'status' => false,
|
||
'msg' => sprintf(
|
||
/* translators: %s AdSense account ID */
|
||
esc_html__( 'invalid response received while renewing access token for "%s"', 'advanced-ads' ),
|
||
$account
|
||
) . ' ' . __( 'You could try to connect again under Advanced Ads > Settings > AdSense.', 'advanced-ads' ),
|
||
'raw' => $response['body'],
|
||
];
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Recoke a refresh token
|
||
*/
|
||
public function ajax_revoke_tokken() {
|
||
|
||
$nonce = Params::post( 'nonce', '' );
|
||
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
|
||
die;
|
||
}
|
||
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
|
||
$adsense_id = stripslashes( Params::post( 'adsenseId', '' ) );
|
||
$options = self::get_option();
|
||
|
||
if ( self::use_user_app() ) {
|
||
$token = $options['accounts'][ $adsense_id ]['user_app']['refresh_token'];
|
||
} else {
|
||
$token = $options['accounts'][ $adsense_id ]['default_app']['refresh_token'];
|
||
}
|
||
$url = 'https://accounts.google.com/o/oauth2/revoke?token=' . $token;
|
||
$args = [
|
||
'timeout' => 5,
|
||
'header' => [ 'Content-type' => 'application/x-www-form-urlencoded' ],
|
||
];
|
||
|
||
$response = wp_remote_post( $url, $args );
|
||
|
||
self::log( 'Revoke API access for ca-' . $adsense_id );
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
echo wp_json_encode( [ 'status' => false ] );
|
||
} else {
|
||
global $wpdb;
|
||
$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'advanced_ads_adsense_report_%';" );
|
||
delete_option( 'advanced-ads-adsense-dashboard-filter' );
|
||
|
||
header( 'Content-Type: application/json' );
|
||
unset( $options['accounts'][ $adsense_id ] );
|
||
update_option( self::OPTION_KEY, $options );
|
||
echo wp_json_encode( [ 'status' => true ] );
|
||
}
|
||
}
|
||
die;
|
||
}
|
||
|
||
/**
|
||
* When a user manually adds an ad code, save it
|
||
*/
|
||
public function ajax_save_manual_code() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
|
||
die();
|
||
}
|
||
|
||
if ( ! wp_verify_nonce( Params::request( 'nonce', '' ), 'advads-mapi' ) ) {
|
||
die();
|
||
}
|
||
|
||
$publisher_id = sanitize_text_field( wp_unslash( isset( $_POST['parsed_code']['pubId'] ) ? $_POST['parsed_code']['pubId'] : '' ) );
|
||
if ( ! $this->check_valid_publisher( $publisher_id ) ) {
|
||
wp_send_json_error(
|
||
[
|
||
'message' => __( 'This ad code is from a different AdSense Account', 'advanced-ads' ),
|
||
],
|
||
400
|
||
);
|
||
}
|
||
|
||
if ( empty( $_POST['parsed_code']['slotId'] ) || empty( $_POST['raw_code'] ) ) {
|
||
die();
|
||
}
|
||
|
||
static $options;
|
||
if ( is_null( $options ) ) {
|
||
$options = self::get_option();
|
||
}
|
||
|
||
$slot_id = 'ca-' . $publisher_id . ':' . sanitize_text_field( wp_unslash( $_POST['parsed_code']['slotId'] ) );
|
||
|
||
// phpcs:disable WordPress.Security
|
||
$options['ad_codes'][ $slot_id ] = urldecode( Params::post( 'raw_code', '' ) );
|
||
// phpcs:enable
|
||
|
||
if ( array_key_exists( $slot_id, $options['unsupported_units'] ) ) {
|
||
unset( $options['unsupported_units'][ $slot_id ] );
|
||
}
|
||
|
||
wp_send_json_success( [ 'updated' => update_option( self::OPTION_KEY, $options ) ] );
|
||
}
|
||
|
||
/**
|
||
* Check if the provided AdSense Publisher ID matches the saved ID
|
||
*
|
||
* @param string $pub AdSense Publisher ID.
|
||
*
|
||
* @return bool
|
||
*/
|
||
protected function check_valid_publisher( $pub ) {
|
||
return Advanced_Ads_AdSense_Data::get_instance()->get_adsense_id() === $pub;
|
||
}
|
||
|
||
/**
|
||
* Save ad code reconstructed from ad parameters
|
||
*/
|
||
public function ajax_save_reconstructed_code() {
|
||
$nonce = Params::post( 'nonce', '' );
|
||
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
|
||
die;
|
||
}
|
||
|
||
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
|
||
$code = stripslashes( Params::post( 'code', '' ) );
|
||
$slot = stripslashes( Params::post( 'slot', '' ) );
|
||
$gadsense_data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$adsense_id = $gadsense_data->get_adsense_id();
|
||
$options = self::get_option();
|
||
$options['ad_codes'][ 'ca-' . $adsense_id . ':' . $slot ] = $code;
|
||
update_option( self::OPTION_KEY, $options );
|
||
header( 'Content-Type: application/json' );
|
||
echo wp_json_encode( [ 'status' => true ] );
|
||
}
|
||
die;
|
||
}
|
||
|
||
/**
|
||
* Get ad code for a given unit
|
||
*/
|
||
public function ajax_get_ad_code() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
|
||
wp_send_json_error( 'Unauthorized', 401 );
|
||
}
|
||
|
||
$post_data = wp_unslash( $_POST );
|
||
$nonce = isset( $post_data['nonce'] ) ? $post_data['nonce'] : '';
|
||
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
|
||
$unit = stripslashes( $post_data['unit'] );
|
||
|
||
if ( ! self::use_user_app() ) {
|
||
$quota = $this->get_quota();
|
||
|
||
// No more quota left.
|
||
if ( $quota['count'] < 1 ) {
|
||
header( 'Content-Type: application/json' );
|
||
$quota_msg = $this->get_quota_msg();
|
||
echo wp_json_encode(
|
||
[
|
||
'quota' => 0,
|
||
'quotaMsg' => $quota_msg,
|
||
]
|
||
);
|
||
die;
|
||
}
|
||
}
|
||
|
||
$code = $this->get_ad_code( $unit );
|
||
|
||
/**
|
||
* Ad code is returned as string. Otherwise it's an error
|
||
*/
|
||
if ( is_string( $code ) ) {
|
||
$ad_units = array_filter(
|
||
Advanced_Ads_Network_Adsense::get_instance()->get_external_ad_units(),
|
||
function ( Advanced_Ads_Ad_Network_Ad_Unit $ad_unit ) use ( $unit ) {
|
||
return $ad_unit->id === $unit;
|
||
}
|
||
);
|
||
$ad_unit = reset( $ad_units );
|
||
$response = [
|
||
'code' => $code,
|
||
'type' => self::format_ad_data( $ad_unit, 'type' ),
|
||
];
|
||
|
||
/**
|
||
* Add quota info for default API creds
|
||
*/
|
||
if ( ! self::use_user_app() ) {
|
||
$quota = $this->get_quota();
|
||
$quota_msg = $this->get_quota_msg();
|
||
$response['quota'] = $quota['count'];
|
||
$response['quotaMsg'] = $quota_msg;
|
||
}
|
||
|
||
header( 'Content-Type: application/json' );
|
||
echo wp_json_encode( $response );
|
||
|
||
} else {
|
||
|
||
// return info about the error.
|
||
header( 'Content-Type: application/json' );
|
||
echo wp_json_encode( $code );
|
||
}
|
||
}
|
||
die;
|
||
}
|
||
|
||
/**
|
||
* Dismiss an account alert
|
||
*/
|
||
public function ajax_dismiss_alert() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
|
||
die;
|
||
}
|
||
$nonce = Params::post( 'nonce', '' );
|
||
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
|
||
$account = stripslashes( Params::post( 'account', '' ) );
|
||
$id = stripslashes( Params::post( 'id', '' ) );
|
||
$options = self::get_option();
|
||
|
||
$items = [];
|
||
|
||
// the account exists.
|
||
if ( isset( $options['accounts'][ $account ] ) ) {
|
||
// the alert exists.
|
||
if ( isset( $options['accounts'][ $account ]['alerts']['items'][ $id ] ) ) {
|
||
unset( $options['accounts'][ $account ]['alerts']['items'][ $id ] );
|
||
|
||
update_option( self::OPTION_KEY, $options );
|
||
$items = $options['accounts'][ $account ]['alerts']['items'];
|
||
}
|
||
}
|
||
wp_send_json(
|
||
[
|
||
'status' => true,
|
||
'alerts' => $items,
|
||
'length' => count( $items ),
|
||
]
|
||
);
|
||
}
|
||
die;
|
||
}
|
||
|
||
/**
|
||
* Get / Update the list of alerts on an AdSense account.
|
||
*/
|
||
public function ajax_get_account_alerts() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
|
||
die;
|
||
}
|
||
|
||
$nonce = Params::post( 'nonce', '' );
|
||
if ( false !== wp_verify_nonce( $nonce, 'mapi-alerts' ) ) {
|
||
$account = stripslashes( Params::post( 'account', '' ) );
|
||
$options = self::get_option();
|
||
|
||
// the account exists.
|
||
if ( isset( $options['accounts'][ $account ] ) && self::has_token( $account ) ) {
|
||
$access_token = self::get_access_token( $account );
|
||
$url = str_replace( 'PUBID', $account, self::ALERTS_URL );
|
||
|
||
if ( isset( $_POST['inlineAttempt'] ) ) {
|
||
if ( ! is_array( $options['accounts'][ $account ]['alerts'] ) ) {
|
||
$options['accounts'][ $account ]['alerts'] = [];
|
||
}
|
||
$options['accounts'][ $account ]['alerts']['inlineAttempt'] = time();
|
||
update_option( self::OPTION_KEY, $options );
|
||
}
|
||
|
||
// the token is valid.
|
||
if ( ! isset( $access_token['msg'] ) ) {
|
||
$headers = [
|
||
'Authorization' => 'Bearer ' . $access_token,
|
||
];
|
||
$response = wp_remote_get( $url, [ 'headers' => $headers ] );
|
||
|
||
$this->log( 'Get AdSense alerts for ' . $account );
|
||
|
||
// the HTTP response is not an error.
|
||
if ( ! is_wp_error( $response ) ) {
|
||
$alerts = json_decode( $response['body'], true );
|
||
|
||
// the response body is valid.
|
||
if ( is_array( $alerts ) && isset( $alerts['alerts'] ) ) {
|
||
$items = [];
|
||
foreach ( $alerts['alerts'] as $item ) {
|
||
// Do not store alerts of type "INFO".
|
||
if ( strcasecmp( $item['severity'], 'INFO' ) !== 0 ) {
|
||
$items[ wp_generate_password( 6, false ) ] = $item;
|
||
}
|
||
}
|
||
|
||
// filter alerts that are not relevant to the user.
|
||
$items = self::filter_account_alerts( $items );
|
||
|
||
$alerts_array = [
|
||
'items' => $items,
|
||
'lastCheck' => time(),
|
||
];
|
||
|
||
$options['accounts'][ $account ]['alerts'] = $alerts_array;
|
||
update_option( self::OPTION_KEY, $options );
|
||
$results = [
|
||
'status' => true,
|
||
'alerts' => $items,
|
||
'length' => count( $items ),
|
||
];
|
||
header( 'Content-Type:application/json' );
|
||
echo wp_json_encode( $results );
|
||
} else {
|
||
$results = [
|
||
'status' => false,
|
||
'msg' => esc_html__( 'Invalid response body while retrieving account alerts', 'advanced-ads' ),
|
||
];
|
||
header( 'Content-Type:application/json' );
|
||
echo wp_json_encode( $results );
|
||
}
|
||
} else {
|
||
$results = [
|
||
'status' => false,
|
||
'msg' => esc_html__( 'error while retrieving account alerts', 'advanced-ads' ),
|
||
'raw' => $response->get_error_message(),
|
||
];
|
||
header( 'Content-Type:application/json' );
|
||
echo wp_json_encode( $results );
|
||
}
|
||
} else {
|
||
// return the original error info.
|
||
return $access_token;
|
||
}
|
||
} else {
|
||
header( 'Content-Type:application/json' );
|
||
echo wp_json_encode( [ 'status' => false ] );
|
||
}
|
||
}
|
||
die;
|
||
}
|
||
|
||
/**
|
||
* Get / Update the ad unit list for a given ad client. The corresponding <select /> input used in the ad selector is passed as a fied of an array
|
||
*/
|
||
public function ajax_get_adUnits() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
|
||
die;
|
||
}
|
||
$post_vars = wp_unslash( $_POST );
|
||
$nonce = isset( $post_vars['nonce'] ) ? wp_strip_all_tags( $post_vars['nonce'] ) : '';
|
||
|
||
if ( wp_verify_nonce( $nonce, 'advads-mapi' ) && isset( $post_vars['account'] ) ) {
|
||
$account = wp_strip_all_tags( stripslashes( $post_vars['account'] ) );
|
||
$units = self::get_ad_units( $account );
|
||
|
||
if ( isset( $units['done'] ) && true === $units['done'] ) {
|
||
ob_start();
|
||
Advanced_Ads_AdSense_Admin::get_mapi_ad_selector();
|
||
$ad_selector = ob_get_clean();
|
||
|
||
$response = [
|
||
'status' => true,
|
||
'html' => $ad_selector,
|
||
];
|
||
|
||
/**
|
||
* Add quota info for default API creds
|
||
*/
|
||
if ( ! self::use_user_app() ) {
|
||
$quota = $this->get_quota();
|
||
$quota_msg = $this->get_quota_msg();
|
||
$response['quota'] = $quota['count'];
|
||
$response['quotaMsg'] = $quota_msg;
|
||
}
|
||
} else {
|
||
// Return the error info array.
|
||
$response = $units;
|
||
}
|
||
header( 'Content-Type: application/json' );
|
||
echo wp_json_encode( $response );
|
||
}
|
||
die;
|
||
}
|
||
|
||
/**
|
||
* Save account and token after account selection MCN.
|
||
*/
|
||
public function ajax_account_selected() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
|
||
die;
|
||
}
|
||
$post_data = wp_unslash( $_POST );
|
||
$nonce = $post_data['nonce'] ?? '';
|
||
|
||
if ( ! wp_verify_nonce( $nonce, 'advads-mapi' ) && ! check_ajax_referer( 'advanced_ads_wizard', 'nonce' ) ) {
|
||
wp_send_json_error( 'Not Authorized', 401 );
|
||
}
|
||
|
||
$token_data = wp_unslash( $post_data['token_data'] );
|
||
$account = wp_unslash( $post_data['account'] );
|
||
|
||
if ( $token_data && $account ) {
|
||
self::save_token_from_data( $token_data, $account );
|
||
wp_send_json_success(
|
||
[
|
||
'status' => true,
|
||
'account' => $account,
|
||
],
|
||
200
|
||
);
|
||
}
|
||
$error = 'Token data missing';
|
||
if ( $token_data ) {
|
||
$error = 'No account provided';
|
||
}
|
||
|
||
wp_send_json_success(
|
||
[
|
||
'status' => false,
|
||
'error_msg' => $error,
|
||
],
|
||
200
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Get AdSense account details from a new access token.
|
||
*/
|
||
public function ajax_get_account_details() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
|
||
wp_send_json_error( 'Unauthorized', 401 );
|
||
}
|
||
|
||
$post_data = wp_unslash( $_POST );
|
||
$nonce = $post_data['nonce'] ?? '';
|
||
|
||
if ( ! wp_verify_nonce( $nonce, 'advads-mapi' ) && ! check_ajax_referer( 'advanced_ads_wizard', 'nonce' ) ) {
|
||
wp_send_json_error( 'Not Authorized', 401 );
|
||
}
|
||
|
||
$url = 'https://adsense.googleapis.com/v2/accounts';
|
||
$list_child_url = 'https://adsense.googleapis.com/v2/accounts/%pubid%:listChildAccounts';
|
||
$token_data = wp_unslash( $post_data['token_data'] );
|
||
|
||
if ( ! is_array( $token_data ) ) {
|
||
wp_send_json(
|
||
[
|
||
'status' => false,
|
||
'error_msg' => esc_html__( 'No token provided. Token data needed to get account details.', 'advanced-ads' ),
|
||
]
|
||
);
|
||
}
|
||
|
||
$headers = [ 'Authorization' => 'Bearer ' . $token_data['access_token'] ];
|
||
$response = wp_remote_get( $url, [ 'headers' => $headers ] );
|
||
|
||
self::log( 'Get account details from new access token' );
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
wp_send_json(
|
||
[
|
||
'status' => false,
|
||
'error_msg' => $response->get_error_message(),
|
||
]
|
||
);
|
||
}
|
||
if ( trim( $response['body'] ) === '{}' ) {
|
||
// No AdSense account.
|
||
$options = self::get_option();
|
||
$options['connect_error'] = [
|
||
'message' => esc_html__( 'No AdSense account data found.', 'advanced-ads' ),
|
||
'reason' => 'noAdsenseData',
|
||
];
|
||
update_option( self::OPTION_KEY, $options );
|
||
wp_send_json_error( [ 'error' => esc_html__( 'No AdSense account data found.', 'advanced-ads' ) ], 404 );
|
||
}
|
||
|
||
$accounts = json_decode( trim( $response['body'] ), true );
|
||
|
||
if ( isset( $accounts['accounts'] ) ) {
|
||
$pub_id = explode( '/', $accounts['accounts'][0]['name'] )[1];
|
||
$child_accounts = wp_remote_get( str_replace( '%pubid%', $pub_id, $list_child_url ), [ 'headers' => $headers ] );
|
||
|
||
if ( is_wp_error( $child_accounts ) ) {
|
||
wp_send_json_error( $child_accounts, 500 );
|
||
}
|
||
$accounts_list = json_decode( trim( $child_accounts['body'] ), true );
|
||
if ( trim( $child_accounts['body'] ) === '{}' ) {
|
||
// Standard AdSense account.
|
||
self::save_token_from_data( $token_data, $accounts['accounts'][0] );
|
||
wp_send_json_success(
|
||
[
|
||
'reload' => true,
|
||
'account' => self::get_account_details( $accounts['accounts'][0] ),
|
||
]
|
||
);
|
||
}
|
||
|
||
if ( null !== $accounts_list ) {
|
||
// Network account.
|
||
$details = [];
|
||
$html = '';
|
||
$details[ $pub_id ] = [
|
||
'id' => $pub_id,
|
||
'name' => $accounts['accounts'][0]['displayName'],
|
||
];
|
||
$html .= sprintf( '<option value="%1$s">%2$s [%3$s]</option>', esc_attr( $pub_id ), esc_html( $accounts['accounts'][0]['displayName'] ), esc_html( $pub_id ) );
|
||
|
||
foreach ( $accounts_list['accounts'] as $item ) {
|
||
$account_id = explode( '/', $item['name'] )[1];
|
||
$details[ $account_id ] = [
|
||
'id' => $account_id,
|
||
'name' => $item['displayName'],
|
||
];
|
||
$html .= sprintf( '<option value="%1$s">%2$s [%3$s]</option>', esc_attr( $account_id ), esc_html( $item['displayName'] ), esc_html( $account_id ) );
|
||
}
|
||
wp_send_json_success(
|
||
[
|
||
'details' => $details,
|
||
'html' => $html,
|
||
'token_data' => $token_data,
|
||
]
|
||
);
|
||
}
|
||
wp_send_json_error( [ 'message' => 'unexpected response - get child accounts' ], 400 );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Submit Google API confirmation code. Save the token and update ad client list.
|
||
*/
|
||
public function ajax_confirm_code() {
|
||
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
|
||
die;
|
||
}
|
||
|
||
$nonce = wp_unslash( Params::post( 'nonce' ) ) ?? '';
|
||
|
||
if ( ! wp_verify_nonce( $nonce, 'advads-mapi' ) && ! check_ajax_referer( 'advanced_ads_wizard', 'nonce' ) ) {
|
||
wp_send_json_error( 'Not Authorized', 401 );
|
||
}
|
||
|
||
$code = urldecode( wp_unslash( Params::post( 'code' ) ) );
|
||
$cid = self::CID;
|
||
$cs = self::CS;
|
||
|
||
$use_user_app = self::use_user_app();
|
||
|
||
if ( $use_user_app ) {
|
||
$cid = ADVANCED_ADS_MAPI_CID;
|
||
$cs = ADVANCED_ADS_MAPI_CIS;
|
||
}
|
||
|
||
$code_url = 'https://www.googleapis.com/oauth2/v4/token';
|
||
$redirect_uri = self::REDIRECT_URI;
|
||
$grant_type = 'authorization_code';
|
||
|
||
$args = [
|
||
'timeout' => 10,
|
||
'body' => [
|
||
'code' => $code,
|
||
'client_id' => $cid,
|
||
'client_secret' => $cs,
|
||
'redirect_uri' => $redirect_uri,
|
||
'grant_type' => $grant_type,
|
||
],
|
||
];
|
||
|
||
$response = wp_remote_post( $code_url, $args );
|
||
|
||
self::log( 'Confirm authorization code' );
|
||
|
||
if ( is_wp_error( $response ) ) {
|
||
wp_send_json_error(
|
||
[
|
||
'status' => false,
|
||
'msg' => 'error while submitting code',
|
||
'raw' => $response->get_error_message(),
|
||
],
|
||
$response->get_error_code()
|
||
);
|
||
}
|
||
|
||
$token = json_decode( $response['body'], true );
|
||
|
||
if ( null !== $token && isset( $token['refresh_token'] ) ) {
|
||
$expires = time() + absint( $token['expires_in'] );
|
||
$token['expires'] = $expires;
|
||
wp_send_json_success(
|
||
[
|
||
'status' => true,
|
||
'token_data' => $token,
|
||
],
|
||
200
|
||
);
|
||
}
|
||
|
||
wp_send_json_error(
|
||
[
|
||
'status' => false,
|
||
'response_body' => $response['body'],
|
||
],
|
||
400
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Enqueue admin scripts
|
||
*
|
||
* @param string $hook current page hook.
|
||
*/
|
||
public function admin_scripts( $hook ) {
|
||
if ( 'advanced-ads_page_advanced-ads-settings' === $hook ) {
|
||
wp_enqueue_script( 'gasense/mapi/settings', GADSENSE_BASE_URL . 'admin/assets/js/mapi-settings.js', [ 'jquery', 'wp-util' ], ADVADS_VERSION );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Print alert data in admin footer
|
||
*/
|
||
public function admin_footer() {
|
||
$data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$adsense_id = $data->get_adsense_id();
|
||
$has_token = self::has_token( $adsense_id );
|
||
$alerts = self::get_stored_account_alerts( $adsense_id );
|
||
$refresh_alerts = false;
|
||
|
||
// default value, never checked for alerts.
|
||
if ( [] === $alerts && $has_token ) {
|
||
$refresh_alerts = true;
|
||
}
|
||
if ( $has_token && is_array( $alerts ) ) {
|
||
// Check weekly for alerts.
|
||
if ( isset( $alerts['lastCheck'] ) && time() > absint( $alerts['lastCheck'] ) + DAY_IN_SECONDS * 7 ) {
|
||
$refresh_alerts = true;
|
||
}
|
||
// Only try to get the alerts in the background once a day.
|
||
if ( isset( $alerts['inlineAttempt'] ) && time() < $alerts['inlineAttempt'] + DAY_IN_SECONDS ) {
|
||
$refresh_alerts = false;
|
||
}
|
||
}
|
||
if ( $refresh_alerts ) {
|
||
$nonce = wp_create_nonce( 'mapi-alerts' );
|
||
?>
|
||
<input type="hidden" id="advads-mapi-refresh-alerts" />
|
||
<script type="text/javascript">
|
||
;(function($) {
|
||
|
||
$( '#mapi-alerts-overlay' ).css( 'display', 'block' );
|
||
|
||
var pubId = $( '#adsense-id' ).val();
|
||
$.ajax({
|
||
url: ajaxurl,
|
||
type: 'post',
|
||
data: {
|
||
action: 'advads-mapi-get-alerts',
|
||
account: '<?php echo esc_html( $adsense_id ); ?>',
|
||
nonce: '<?php echo esc_html( $nonce ); ?>',
|
||
inlineAttempt: 1,
|
||
},
|
||
success:function(response, status, XHR) {
|
||
if ( 'undefined' != typeof response.alerts ) {
|
||
$( '#advads-mapi-refresh-alerts' ).trigger( 'advadsMapiRefreshAlerts', [response] );
|
||
}
|
||
$( '#mapi-alerts-overlay' ).css( 'display', 'none' );
|
||
},
|
||
error:function(request, status, error) {
|
||
$( '#mapi-alerts-overlay' ).css( 'display', 'none' );
|
||
},
|
||
});
|
||
|
||
})(window.jQuery);
|
||
</script>
|
||
<?php
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Logs a task to a file if ADVANCED_ADS_LOG_ADSENSE_API constant is defined and true.
|
||
*
|
||
* @param string $task The task to be logged. Default is 'No task provided'.
|
||
*
|
||
* @return void
|
||
*/
|
||
public static function log( $task = 'No task provided' ): void {
|
||
if ( ! defined( 'ADVANCED_ADS_LOG_ADSENSE_API' ) || ! ADVANCED_ADS_LOG_ADSENSE_API ) {
|
||
return;
|
||
}
|
||
|
||
$message = date_i18n( '[Y-m-d H:i:s]' ) . ' ' . $task . "\n";
|
||
error_log( $message, 3, WP_CONTENT_DIR . '/advanced-ads-google-api-requests.log' );
|
||
}
|
||
|
||
/**
|
||
* Sort ad units list alphabetically
|
||
*
|
||
* @param array $adunits The array of ad units to be sorted.
|
||
*
|
||
* @return array The sorted array of ad units.
|
||
*/
|
||
public static function get_sorted_adunits( $adunits ): array {
|
||
$units_sorted_by_name = [];
|
||
$units_by_id = [];
|
||
foreach ( $adunits as $unit ) {
|
||
$units_sorted_by_name[ $unit['name'] ] = $unit['id'];
|
||
$units_by_id[ $unit['id'] ] = $unit;
|
||
}
|
||
ksort( $units_sorted_by_name );
|
||
$units_sorted_by_name = array_flip( $units_sorted_by_name );
|
||
|
||
$results = [];
|
||
foreach ( $units_sorted_by_name as $id => $name ) {
|
||
$results[ $name ] = $units_by_id[ $id ];
|
||
}
|
||
|
||
return $results;
|
||
}
|
||
|
||
/**
|
||
* Format ad type and size strings from Google for display
|
||
*
|
||
* @param Advanced_Ads_Ad_Network_Ad_Unit $ad_unit the ad unit for which to format the details.
|
||
* @param string $format takes either type or size.
|
||
*
|
||
* @return string
|
||
*/
|
||
public static function format_ad_data( Advanced_Ads_Ad_Network_Ad_Unit $ad_unit, $format = 'type' ) {
|
||
if ( 'type' === $format ) {
|
||
$str = $ad_unit->display_type;
|
||
$options = self::get_option();
|
||
if ( array_key_exists( $ad_unit->id, $options['ad_codes'] ) ) {
|
||
preg_match_all( '/data-ad-format="(?<format>.+?)"|data-ad-layout="(?<layout>.+?)"/', $options['ad_codes'][ $ad_unit->id ], $matches );
|
||
$format = array_filter( $matches['format'] );
|
||
$layout = array_filter( $matches['layout'] );
|
||
$format = reset( $format );
|
||
$layout = reset( $layout );
|
||
if ( empty( $format ) ) {
|
||
$format = '';
|
||
}
|
||
if ( empty( $layout ) ) {
|
||
$layout = '';
|
||
}
|
||
|
||
if ( 'autorelaxed' === $format ) {
|
||
$str = _x( 'Multiplex', 'AdSense ad type', 'advanced-ads' );
|
||
} elseif ( 'fluid' === $format ) {
|
||
if ( 'in-article' === $layout ) {
|
||
$str = _x( 'In-article', 'AdSense ad type', 'advanced-ads' );
|
||
} else {
|
||
$str = _x( 'In-feed', 'AdSense ad type', 'advanced-ads' );
|
||
}
|
||
}
|
||
}
|
||
|
||
switch ( $str ) {
|
||
case 'DISPLAY':
|
||
$str = _x( 'Display', 'AdSense ad type', 'advanced-ads' );
|
||
break;
|
||
case 'LINK':
|
||
$str = _x( 'Link', 'AdSense ad type', 'advanced-ads' );
|
||
break;
|
||
case 'MATCHED_CONTENT':
|
||
$str = _x( 'Multiplex', 'AdSense ad type', 'advanced-ads' );
|
||
break;
|
||
case 'ARTICLE':
|
||
$str = _x( 'In-article', 'AdSense ad type', 'advanced-ads' );
|
||
break;
|
||
case 'FEED':
|
||
$str = _x( 'In-feed', 'AdSense ad type', 'advanced-ads' );
|
||
break;
|
||
case 'TYPE_UNSPECIFIED':
|
||
default:
|
||
}
|
||
} elseif ( 'size' === $format ) {
|
||
// size.
|
||
$str = '1x3' === $ad_unit->display_size
|
||
? 'Responsive' : $ad_unit->display_size;
|
||
|
||
if ( strpos( $str, 'SIZE_' ) === 0 ) {
|
||
$str = str_replace( '_', 'x', substr( $str, 5 ) );
|
||
}
|
||
$str = ucfirst( strtolower( $str ) );
|
||
} else {
|
||
$str = '';
|
||
}
|
||
|
||
return $str;
|
||
}
|
||
|
||
/**
|
||
* Check if the credential are the default ones or from user's app
|
||
*/
|
||
public static function use_user_app() {
|
||
if (
|
||
( defined( 'ADVANCED_ADS_MAPI_CID' ) && '' !== ADVANCED_ADS_MAPI_CID ) &&
|
||
( defined( 'ADVANCED_ADS_MAPI_CIS' ) && '' !== ADVANCED_ADS_MAPI_CIS )
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Checks if a Google AdSense token exists for the specified AdSense ID.
|
||
*
|
||
* @param string $adsense_id The AdSense ID to check for.
|
||
*
|
||
* @return bool Returns true if a token exists, false otherwise.
|
||
*/
|
||
public static function has_token( $adsense_id = '' ): bool {
|
||
if ( empty( $adsense_id ) ) {
|
||
return false;
|
||
}
|
||
|
||
$options = self::get_option();
|
||
$look_for = self::use_user_app() ? 'user_app' : 'default_app';
|
||
|
||
if (
|
||
isset( $options['accounts'][ $adsense_id ] ) &&
|
||
! empty( $options['accounts'][ $adsense_id ][ $look_for ]['refresh_token'] )
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Save token obtained from confirmation code
|
||
*
|
||
* @param string $token access token data.
|
||
* @param array $details selected account details.
|
||
*/
|
||
public static function save_token_from_data( $token, $details ) {
|
||
$options = self::get_option();
|
||
$adsense_id = isset( $details['id'] ) ? $details['id'] : explode( '/', $details['name'] )[1];
|
||
|
||
if ( ! isset( $options['accounts'][ $adsense_id ] ) ) {
|
||
$options['accounts'][ $adsense_id ] = self::$empty_account_data;
|
||
}
|
||
if ( self::use_user_app() ) {
|
||
$options['accounts'][ $adsense_id ]['user_app'] = [
|
||
'access_token' => $token['access_token'],
|
||
'refresh_token' => $token['refresh_token'],
|
||
'expires' => $token['expires'],
|
||
'token_type' => $token['token_type'],
|
||
];
|
||
} else {
|
||
$options['accounts'][ $adsense_id ]['default_app'] = [
|
||
'access_token' => $token['access_token'],
|
||
'refresh_token' => $token['refresh_token'],
|
||
'expires' => $token['expires'],
|
||
'token_type' => $token['token_type'],
|
||
];
|
||
}
|
||
$options['accounts'][ $adsense_id ]['details'] = [
|
||
'id' => $adsense_id,
|
||
'name' => isset( $details['displayName'] ) ? $details['displayName'] : $details['name'],
|
||
];
|
||
$options['connect_error'] = [];
|
||
update_option( self::OPTION_KEY, $options );
|
||
|
||
$gadsense_data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$gadsense_options = $gadsense_data->get_options();
|
||
$gadsense_options['adsense-id'] = $adsense_id;
|
||
update_option( GADSENSE_OPT_NAME, $gadsense_options );
|
||
}
|
||
|
||
/**
|
||
* Get a list of stored alerts for a given AdSense account.
|
||
*
|
||
* @param string $pub_id the publisher account.
|
||
* @return array $alerts
|
||
*/
|
||
public static function get_stored_account_alerts( $pub_id = '' ) {
|
||
if ( empty( $pub_id ) ) {
|
||
return false;
|
||
}
|
||
|
||
$options = self::get_option();
|
||
|
||
if ( isset( $options['accounts'][ $pub_id ] ) ) {
|
||
$account = [];
|
||
if ( isset( $options['accounts'][ $pub_id ]['alerts'] ) && is_array( $options['accounts'][ $pub_id ]['alerts'] ) ) {
|
||
$alerts = $options['accounts'][ $pub_id ]['alerts'];
|
||
$account = self::filter_stored_account_alerts( $alerts );
|
||
}
|
||
|
||
return $account;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* We filter out specific alerts from the AdSense account when they are
|
||
* - duplicates
|
||
* - irrelevant when placing ads in the frontend
|
||
*
|
||
* @param array $alert_items alerts.
|
||
* @param null|array $disabled_alerts additional disabled alerts.
|
||
* @return array filtered alert items.
|
||
*/
|
||
public static function filter_account_alerts( array $alert_items, $disabled_alerts = null ) {
|
||
if ( empty( $alert_items ) || ! is_array( $alert_items ) ) {
|
||
return $alert_items;
|
||
}
|
||
|
||
// the message IDs we don’t even import from AdSense.
|
||
$disabled_adsense_alerts = [
|
||
'SELLERS_JSON_CONSENT', // AdSense message: We encourage you to publish your seller information in the Google sellers.json file. Visit the account settings page to review your current visibility status.
|
||
'REPORTING_HORIZON_LEGACY_DATA_NOTICE', // AdSense message: Data older than three years is no longer available in Reporting. This data can be downloaded for a limited time.
|
||
];
|
||
|
||
// additional messages to disable. Useful if the function is used in different situations.
|
||
if ( ! empty( $disabled_alerts ) && is_array( $disabled_alerts ) ) {
|
||
$disabled_adsense_alerts = array_merge( $disabled_adsense_alerts, $disabled_alerts );
|
||
}
|
||
|
||
// remove alerts based on specific IDs.
|
||
foreach ( $alert_items as $internal_id => $item ) {
|
||
if (
|
||
( isset( $item['id'] ) && in_array( $item['id'], $disabled_adsense_alerts, true ) )
|
||
|| ( isset( $item['type'] ) && in_array( str_replace( '-', '_', strtoupper( $item['type'] ) ), $disabled_adsense_alerts, true ) )
|
||
) {
|
||
unset( $alert_items[ $internal_id ] );
|
||
}
|
||
}
|
||
|
||
return $alert_items;
|
||
}
|
||
|
||
/**
|
||
* Remove alerts dynamically when showing them.
|
||
* only applies to stored alerts and not when they are loaded directly from AdSense
|
||
*
|
||
* @param array $alerts Alert options.
|
||
* @return array $alerts Modified alert options.
|
||
*/
|
||
public static function filter_stored_account_alerts( array $alerts ) {
|
||
if ( empty( $alerts['items'] ) || ! is_array( $alerts['items'] ) ) {
|
||
return $alerts;
|
||
}
|
||
|
||
$disabled_alerts = [];
|
||
|
||
/**
|
||
* Asside from the basic filter, we also filter out some messages only from showing up while we still import them
|
||
* This allows us to show them only under related conditions
|
||
*/
|
||
// Remove `ads.txt` related alerts if the file is displayed to visitors.
|
||
if ( Advanced_Ads_Ads_Txt_Admin::is_displayed() ) {
|
||
$disabled_alerts = [ 'ALERT_TYPE_ADS_TXT_UNAUTHORIZED', 'ADS_TXT_MISSING', 'ADS_TXT_ISSUES' ];
|
||
}
|
||
|
||
$alerts['items'] = self::filter_account_alerts( $alerts['items'], $disabled_alerts );
|
||
|
||
return $alerts;
|
||
}
|
||
|
||
/**
|
||
* Checks if there is any AdSense warning for the currently connected AdSense account.
|
||
*
|
||
* @param array $messages The array of messages.
|
||
*
|
||
* @return array The modified array.
|
||
*/
|
||
public static function adsense_warnings_check( $messages ) {
|
||
$data = Advanced_Ads_AdSense_Data::get_instance();
|
||
$adsense_id = $data->get_adsense_id();
|
||
$alerts = self::get_stored_account_alerts( $adsense_id );
|
||
|
||
if ( ! is_array( $messages ) ) {
|
||
$messages = [];
|
||
}
|
||
|
||
if ( ! empty( $alerts ) && ! empty( $alerts['items'] ) ) {
|
||
$messages[] = sprintf(
|
||
wp_kses(
|
||
/* translators: %s admin setting page link */
|
||
__( 'There are one or more warnings about the currently linked AdSense account. You can view them <a href="%s">here</a>', 'advanced-ads' ),
|
||
[ 'a' => [ 'href' => true ] ]
|
||
),
|
||
esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' ) )
|
||
);
|
||
}
|
||
return $messages;
|
||
}
|
||
|
||
/**
|
||
* Get the class's option
|
||
*/
|
||
public static function get_option() {
|
||
$options = get_option( self::OPTION_KEY, [] );
|
||
if ( ! is_array( $options ) ) {
|
||
$options = [];
|
||
}
|
||
return $options + self::$default_options;
|
||
}
|
||
|
||
/**
|
||
* Get the URL to the AdSense error page
|
||
*
|
||
* @param string $code Add the error code to the URL.
|
||
*
|
||
* @return string The entire text with the url.
|
||
*/
|
||
public static function get_adsense_error_link( $code = '' ) {
|
||
if ( ! empty( $code ) ) {
|
||
$code = '-' . $code;
|
||
}
|
||
|
||
return sprintf(
|
||
/* translators: %1$s is an anchor (link) opening tag, %2$s is the closing tag. */
|
||
esc_attr__( 'Learn more about AdSense account issues %1$shere%2$s.', 'advanced-ads' ),
|
||
'<a href="https://wpadvancedads.com/adsense-errors/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adsense-error' . $code . '" target="_blank">',
|
||
'</a>'
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Get custom account connection error message list.
|
||
*/
|
||
public static function get_connect_error_messages() {
|
||
$messages = [];
|
||
$health_class = Advanced_Ads_Ad_Health_Notices::get_instance();
|
||
foreach ( $health_class->default_notices as $key => $value ) {
|
||
if ( 0 === strpos( $key, 'adsense_connect_' ) ) {
|
||
$messages[ substr( $key, strlen( 'adsense_connect_' ) ) ] = $value['text'];
|
||
}
|
||
}
|
||
return $messages;
|
||
}
|
||
|
||
/**
|
||
* Get custom messages for AdSense alerts.
|
||
*/
|
||
public static function get_adsense_alert_messages() {
|
||
$messages = [];
|
||
$health_class = Advanced_Ads_Ad_Health_Notices::get_instance();
|
||
|
||
foreach ( $health_class->default_notices as $key => $value ) {
|
||
if ( 0 === strpos( $key, 'adsense_alert_' ) ) {
|
||
$messages[ substr( $key, strlen( 'adsense_alert_' ) ) ] = $value['text'];
|
||
}
|
||
}
|
||
return $messages;
|
||
}
|
||
|
||
/**
|
||
* Get class instance
|
||
*
|
||
* @return Advanced_Ads_AdSense_MAPI
|
||
*/
|
||
public static function get_instance() {
|
||
if ( null === self::$instance ) {
|
||
self::$instance = new self();
|
||
}
|
||
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* Extract basic account info from the data sent by Google
|
||
*
|
||
* @param array $account account data from Google.
|
||
*
|
||
* @return array
|
||
*/
|
||
private static function get_account_details( $account ) {
|
||
return [
|
||
'id' => str_replace( 'accounts/', '', $account['name'] ),
|
||
'name' => $account['displayName'],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Get a hint for an error object that was received from AdSense
|
||
*
|
||
* @param string $reason The reason from the response's error.
|
||
*
|
||
* @return string|bool if there is a hint for this reason, a string containing the hint will be returned.
|
||
*/
|
||
final public static function get_adsense_error_hint( $reason ) {
|
||
|
||
if ( 'authError' === $reason ) {
|
||
return sprintf(
|
||
/* translators: 1: A link to the settings page 2: The name of an ad network */
|
||
__( 'Please try to <a href="%1$s" target="_blank">reconnect to your %2$s account</a>.', 'advanced-ads' ),
|
||
admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' ),
|
||
'AdSense'
|
||
);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|