: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$options['accounts'][ $account ]['ad_units'] = $new_ad_units;
update_option( self::OPTNAME, $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'];
// return all error info [arr]
return $options['accounts'][ $account ]['user_app']['access_token'];
if ( time() > $options['accounts'][ $account ]['default_app']['expires'] ) {
$new_tokens = self::renew_access_token( $account );
if ( $new_tokens['status'] ) {
return $new_tokens['access_token'];
// return all error info [arr]
return $options['accounts'][ $account ]['default_app']['access_token'];
// Account does not exist.
if ( ! empty( $options['accounts'] ) ) {
// There is another account connected.
'msg' => esc_html__( 'It seems that some changes have been made in the Advanced Ads settings. Please refresh this page.', 'advanced-ads' ),
/* 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 ] ),
* Renew the current access token.
* @param array $account AdSense account ID.
public static function renew_access_token( $account ) {
$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';
'refresh_token' => $refresh_token,
'grant_type' => 'refresh_token',
$response = wp_remote_post( $url, $args );
self::log( 'Refresh access token' );
if ( is_wp_error( $response ) ) {
/* translators: %s account id */
'msg' => sprintf( esc_html__( 'error while renewing access token for "%s"', 'advanced-ads' ), $account ),
'raw' => $response->get_error_message(),
$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;
$options['accounts'][ $account ]['default_app']['access_token'] = $tokens['access_token'];
$options['accounts'][ $account ]['default_app']['expires'] = $expires;
update_option( self::OPTNAME, $options );
'access_token' => $tokens['access_token'],
/* translators: %s AdSense account ID */
esc_html__( 'invalid response received while renewing access token for "%s"', 'advanced-ads' ),
) . ' ' . __( 'You could try to connect again under Advanced Ads > Settings > AdSense.', 'advanced-ads' ),
'raw' => $response['body'],
public function ajax_revoke_tokken() {
$nonce = $_POST['nonce'] ?? '';
if ( ! WordPress::user_can( 'advanced_ads_manage_options' ) ) {
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
$adsense_id = stripslashes( $_POST['adsenseId'] );
$options = self::get_option();
if ( self::use_user_app() ) {
$token = $options['accounts'][ $adsense_id ]['user_app']['refresh_token'];
$token = $options['accounts'][ $adsense_id ]['default_app']['refresh_token'];
$url = 'https://accounts.google.com/o/oauth2/revoke?token=' . $token;
'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 json_encode( [ 'status' => false ] );
// remove all the adsense stats
$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::OPTNAME, $options );
echo json_encode( [ 'status' => true ] );
* When a user manually adds an ad code, save it
public function ajax_save_manual_code() {
if ( ! WordPress::user_can( 'advanced_ads_edit_ads' ) ) {
if ( ! wp_verify_nonce( isset( $_POST['nonce'] ) ? $_REQUEST['nonce'] : '', 'advads-mapi' ) ) {
$publisher_id = sanitize_text_field( wp_unslash( isset( $_POST['parsed_code']['pubId'] ) ? $_POST['parsed_code']['pubId'] : '' ) );
if ( ! $this->check_valid_publisher( $publisher_id ) ) {
'message' => __( 'This ad code is from a different AdSense Account', 'advanced-ads' ),
if ( empty( $_POST['parsed_code']['slotId'] ) || empty( $_POST['raw_code'] ) ) {
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( $_POST['raw_code'] );
if ( array_key_exists( $slot_id, $options['unsupported_units'] ) ) {
unset( $options['unsupported_units'][ $slot_id ] );
wp_send_json_success( [ 'updated' => update_option( self::OPTNAME, $options ) ] );
* Check if the provided AdSense Publisher ID matches the saved ID
* @param string $pub AdSense Publisher ID.
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 = $_POST['nonce'] ?? '';
if ( ! WordPress::user_can( 'advanced_ads_edit_ads' ) ) {
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
$code = stripslashes( $_POST['code'] );
$slot = stripslashes( $_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::OPTNAME, $options );
header( 'Content-Type: application/json' );
echo json_encode( [ 'status' => true ] );
* Get ad code for a given unit
public function ajax_get_ad_code() {
if ( ! WordPress::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();
if ( $quota['count'] < 1 ) {
header( 'Content-Type: application/json' );
$quota_msg = $this->get_quota_msg();
'quotaMsg' => $quota_msg,
$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 );
'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 );
// return info about the error.
header( 'Content-Type: application/json' );
echo wp_json_encode( $code );
* Dismiss an account alert
public function ajax_dismiss_alert() {
if ( ! WordPress::user_can( 'advanced_ads_manage_options' ) ) {
$nonce = $_POST['nonce'] ?? '';
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
$account = stripslashes( $_POST['account'] );
$id = stripslashes( $_POST['id'] );
$options = self::get_option();
if ( isset( $options['accounts'][ $account ] ) ) {
if ( isset( $options['accounts'][ $account ]['alerts']['items'][ $id ] ) ) {
unset( $options['accounts'][ $account ]['alerts']['items'][ $id ] );
update_option( self::OPTNAME, $options );
$items = $options['accounts'][ $account ]['alerts']['items'];
'length' => count( $items ),
* Get / Update the list of alerts on an AdSense account.
public function ajax_get_account_alerts() {
if ( ! WordPress::user_can( 'advanced_ads_manage_options' ) ) {
$nonce = $_POST['nonce'] ?? '';
if ( false !== wp_verify_nonce( $nonce, 'mapi-alerts' ) ) {
$account = stripslashes( $_POST['account'] );
$options = self::get_option();
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::OPTNAME, $options );
if ( ! isset( $access_token['msg'] ) ) {
'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'] ) ) {
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 );
$options['accounts'][ $account ]['alerts'] = $alerts_array;
update_option( self::OPTNAME, $options );
'length' => count( $items ),
header( 'Content-Type:application/json' );
echo wp_json_encode( $results );
'msg' => esc_html__( 'Invalid response body while retrieving account alerts', 'advanced-ads' ),
header( 'Content-Type:application/json' );
echo wp_json_encode( $results );
'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 );
// return the original error info.
header( 'Content-Type:application/json' );
echo wp_json_encode( [ 'status' => false ] );
* 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 ( ! WordPress::user_can( 'advanced_ads_edit_ads' ) ) {
$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'] ) {
Advanced_Ads_AdSense_Admin::get_mapi_ad_selector();
$ad_selector = ob_get_clean();
* 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;
// Return the error info array.