: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
header( 'Content-Type: application/json' );
echo wp_json_encode( $response );
* Save account and token after account selection MCN.
public function ajax_account_selected() {
if ( ! WordPress::user_can( 'advanced_ads_manage_options' ) ) {
$post_data = wp_unslash( $_POST );
$nonce = isset( $post_data['nonce'] ) ? $post_data['nonce'] : '';
if ( ! wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
die( 'Unauthorized request' );
$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( [ 'status' => true ] );
$error = 'Token data missing';
$error = 'No account provided';
* Get AdSense account details from a new access token.
public function ajax_get_account_details() {
if ( ! WordPress::user_can( 'advanced_ads_manage_options' ) ) {
wp_send_json_error( 'Unauthorized', 401 );
$post_data = wp_unslash( $_POST );
$nonce = isset( $post_data['nonce'] ) ? $post_data['nonce'] : '';
if ( ! wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
wp_send_json_error( 'invalid authorization', 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 ) ) {
'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 ) ) {
'error_msg' => $response->get_error_message(),
if ( trim( $response['body'] ) === '{}' ) {
// Empty, disapproved or other reason.
$options = self::get_option();
$options['connect_error'] = [
'message' => esc_html__( 'No AdSense account data found.', 'advanced-ads' ),
'reason' => 'noAdsenseData',
update_option( self::OPTNAME, $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 ] );
if ( null !== $accounts_list ) {
'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 ] = [
'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 ) );
'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 ( ! WordPress::user_can( 'advanced_ads_manage_options' ) ) {
$nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : '';
if ( false !== wp_verify_nonce( $nonce, 'advads-mapi' ) ) {
$code = urldecode( $_POST['code'] );
$use_user_app = self::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';
'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 ) ) {
'msg' => 'error while submitting code',
'raw' => $response->get_error_message(),
$token = json_decode( $response['body'], true );
if ( null !== $token && isset( $token['refresh_token'] ) ) {
$expires = time() + absint( $token['expires_in'] );
$token['expires'] = $expires;
header( 'Content-Type: application/json' );
header( 'Content-Type: application/json' );
'response_body' => $response['body'],
* @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 = Advanced_Ads_AdSense_MAPI::has_token( $adsense_id );
$alerts = self::get_stored_account_alerts( $adsense_id );
// default value, never checked for alerts.
if ( [] === $alerts && $has_token ) {
if ( $has_token && is_array( $alerts ) ) {
// Check weekly for alerts.
if ( isset( $alerts['lastCheck'] ) && time() > absint( $alerts['lastCheck'] ) + DAY_IN_SECONDS * 7 ) {
// Only try to get the alerts in the background once a day.
if ( isset( $alerts['inlineAttempt'] ) && time() < $alerts['inlineAttempt'] + DAY_IN_SECONDS ) {
$nonce = wp_create_nonce( 'mapi-alerts' );
<input type="hidden" id="advads-mapi-refresh-alerts" />
<script type="text/javascript">
$( '#mapi-alerts-overlay' ).css( 'display', 'block' );
var pubId = $( '#adsense-id' ).val();
action: 'advads-mapi-get-alerts',
account: '<?php echo wp_strip_all_tags( $adsense_id ); ?>',
nonce: '<?php echo wp_strip_all_tags( $nonce ); ?>',
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' );
public static function log( $task = 'No task provided' ) {
if ( ! defined( 'ADVANCED_ADS_LOG_ADSENSE_API' ) || ! ADVANCED_ADS_LOG_ADSENSE_API ) {
$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
public static function get_sorted_adunits( $adunits ) {
$units_sorted_by_name = [];
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 );
foreach ( $units_sorted_by_name as $id => $name ) {
$results[ $name ] = $units_by_id[ $id ];
* 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.
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 ) ) {
if ( empty( $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' );
$str = _x( 'In-feed', 'AdSense ad type', 'advanced-ads' );
$str = _x( 'Display', 'AdSense ad type', 'advanced-ads' );
$str = _x( 'Link', 'AdSense ad type', 'advanced-ads' );
$str = _x( 'Multiplex', 'AdSense ad type', 'advanced-ads' );
$str = _x( 'In-article', 'AdSense ad type', 'advanced-ads' );
$str = _x( 'In-feed', 'AdSense ad type', 'advanced-ads' );
} elseif ( 'size' === $format ) {
$str = $ad_unit->display_size === '1x3' ? 'Responsive' : $ad_unit->display_size;
if ( strpos( $str, 'SIZE_' ) === 0 ) {
$str = str_replace( '_', 'x', substr( $str, 5 ) );
$str = ucfirst( strtolower( $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 ) ) {
public static function has_token( $adsense_id = '' ) {
if ( empty( $adsense_id ) ) {
$options = self::get_option();
if ( self::use_user_app() ) {
if ( isset( $options['accounts'][ $adsense_id ] ) && ! empty( $options['accounts'][ $adsense_id ]['user_app']['refresh_token'] ) ) {
} elseif ( isset( $options['accounts'][ $adsense_id ] ) && ! empty( $options['accounts'][ $adsense_id ]['default_app']['refresh_token'] ) ) {
* 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'],
$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'] = [
'name' => isset( $details['displayName'] ) ? $details['displayName'] : $details['name'],
$options['connect_error'] = [];
update_option( self::OPTNAME, $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.
public static function get_stored_account_alerts( $pub_id = '' ) {
if ( empty( $pub_id ) ) {
$options = self::get_option();
if ( isset( $options['accounts'][ $pub_id ] ) ) {
if ( isset( $options['accounts'][ $pub_id ]['alerts'] ) && is_array( $options['accounts'][ $pub_id ]['alerts'] ) ) {
$alerts = $options['accounts'][ $pub_id ]['alerts'];
return self::filter_stored_account_alerts( $alerts );
* We filter out specific alerts from the AdSense account when they are
* - 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 ) ) {
// 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 ) {
( 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 ) )