HEX
Server: Apache/2.4.65 (Debian)
System: Linux 88f31f35b0b8 6.1.0-38-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.147-1 (2025-08-02) x86_64
User: www-data (33)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/advanced-ads/includes/admin/class-ajax.php
<?php
/**
 * AJAX Ads
 *
 * @package AdvancedAds
 * @author  Advanced Ads <info@wpadvancedads.com>
 * @since   1.50.0
 */

namespace AdvancedAds\Admin;

use Advanced_Ads_Pro;
use Advanced_Ads_Privacy;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use Advanced_Ads_Admin_Notices;
use AdvancedAds\Frontend\Stats;
use Advanced_Ads_Admin_Licenses;
use Advanced_Ads_Ad_Blocker_Admin;
use Advanced_Ads_Ad_Health_Notices;
use Advanced_Ads_Display_Conditions;
use Advanced_Ads_Visitor_Conditions;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Arr;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;

defined( 'ABSPATH' ) || exit;

/**
 * Frontend AJAX.
 */
class AJAX implements Integration_Interface {

	/**
	 * Hook into WordPress.
	 *
	 * @return void
	 */
	public function hooks(): void {
		add_action( 'wp_ajax_advads_ad_select', [ $this, 'ad_select' ] );
		add_action( 'wp_ajax_nopriv_advads_ad_select', [ $this, 'ad_select' ] );
		add_action( 'wp_ajax_advads-ad-health-notice-push', [ $this, 'ad_health_notice_push' ] );
		add_action( 'wp_ajax_nopriv_advads-ad-health-notice-push', [ $this, 'ad_health_notice_push' ] );
		add_action( 'wp_ajax_advads_dismiss_welcome', [ $this, 'dismiss_welcome' ] );
		add_action( 'wp_ajax_advads_newsletter', [ $this, 'subscribe_to_newsletter' ] );
		add_action( 'wp_ajax_advads_activate_addon', [ $this, 'activate_add_on' ] );
		add_action( 'wp_ajax_advads-multiple-subscribe', [ $this, 'multiple_subscribe' ] );

		add_action( 'wp_ajax_load_ad_parameters_metabox', [ $this, 'load_ad_parameters_metabox' ] );
		add_action( 'wp_ajax_load_visitor_conditions_metabox', [ $this, 'load_visitor_condition' ] );
		add_action( 'wp_ajax_load_display_conditions_metabox', [ $this, 'load_display_condition' ] );
		add_action( 'wp_ajax_advads-terms-search', [ $this, 'search_terms' ] );
		add_action( 'wp_ajax_advads-authors-search', [ $this, 'search_authors' ] );
		add_action( 'wp_ajax_advads-close-notice', [ $this, 'close_notice' ] );
		add_action( 'wp_ajax_advads-hide-notice', [ $this, 'hide_notice' ] );
		add_action( 'wp_ajax_advads-subscribe-notice', [ $this, 'subscribe' ] );
		add_action( 'wp_ajax_advads-activate-license', [ $this, 'activate_license' ] );
		add_action( 'wp_ajax_advads-deactivate-license', [ $this, 'deactivate_license' ] );
		add_action( 'wp_ajax_advads-adblock-rebuild-assets', [ $this, 'adblock_rebuild_assets' ] );
		add_action( 'wp_ajax_advads-post-search', [ $this, 'post_search' ] );
		add_action( 'wp_ajax_advads-ad-injection-content', [ $this, 'inject_placement' ] );
		add_action( 'wp_ajax_advads-save-hide-wizard-state', [ $this, 'save_wizard_state' ] );
		add_action( 'wp_ajax_advads-adsense-enable-pla', [ $this, 'adsense_enable_pla' ] );
		add_action( 'wp_ajax_advads-ad-health-notice-display', [ $this, 'ad_health_notice_display' ] );
		add_action( 'wp_ajax_advads-ad-health-notice-push-adminui', [ $this, 'ad_health_notice_push_adminui' ] );
		add_action( 'wp_ajax_advads-ad-health-notice-hide', [ $this, 'ad_health_notice_hide' ] );
		add_action( 'wp_ajax_advads-ad-health-notice-unignore', [ $this, 'ad_health_notice_unignore' ] );
		add_action( 'wp_ajax_advads-ad-health-notice-solved', [ $this, 'ad_health_notice_solved' ] );
		add_action( 'wp_ajax_advads-update-frontend-element', [ $this, 'update_frontend_element' ] );
		add_action( 'wp_ajax_advads-get-block-hints', [ $this, 'get_block_hints' ] );
	}

	/**
	 * Background plugin activation from the add-on box
	 *
	 * @return void
	 */
	public function activate_add_on(): void {
		wp_ajax_activate_plugin();
	}

	/**
	 * Subscribe to the newsletter
	 *
	 * @return void
	 */
	public function subscribe_to_newsletter(): void {
		if ( ! wp_verify_nonce( sanitize_text_field( Params::post( 'nonce' ), '' ), 'advads-newsletter-subscribe' ) ) {
			wp_send_json_error( 'Not Authorized', 401 );
		}
		if ( ! Conditional::user_can( 'advanced_ads_see_interface' ) ) {
			wp_send_json_error(
				[
					/* translators: %s is a URL. */
					'message' => sprintf( __( 'An error occurred. Please use <a href="%s" target="_blank">this form</a> to sign up.', 'advanced-ads' ), 'http://eepurl.com/bk4z4P' ),
				],
				403
			);
		}

		wp_send_json_success( \Advanced_Ads_Admin_Notices::get_instance()->subscribe( 'nl_free_addons' ), 200 );
	}

	/**
	 * Stop showing the welcome after a click on the dismiss icon
	 *
	 * @return void
	 */
	public function dismiss_welcome(): void {
		Welcome::get()->dismiss();
		wp_send_json_success( 'OK', 200 );
	}

	/**
	 * Simple wp ajax interface for ad selection.
	 *
	 * @return void
	 */
	public function ad_select(): void {
		add_filter( 'advanced-ads-output-inline-css', '__return_false' );

		// Allow modules / add-ons to test (this is rather late but should happen before anything important is called).
		do_action( 'advanced-ads-ajax-ad-select-init' );

		$ad_ids      = Params::request( 'ad_ids', [], FILTER_VALIDATE_INT, FILTER_REQUIRE_ARRAY );
		$defered_ads = Params::request( 'deferedAds', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );

		if ( is_array( $ad_ids ) ) {
			foreach ( $ad_ids as $ad_id ) {
				Stats::get()->add_entity( 'ad', is_array( $ad_id ) ? $ad_id['id'] : $ad_id, '' );
			}
		}

		if ( $defered_ads ) {
			$response = [];

			$requests_by_blog = [];
			foreach ( $defered_ads as $request ) {
				$blog_id                        = $request['blog_id'] ?? get_current_blog_id();
				$requests_by_blog[ $blog_id ][] = $request;
			}

			foreach ( $requests_by_blog as $blog_id => $requests ) {
				if ( get_current_blog_id() !== $blog_id && is_multisite() ) {
					switch_to_blog( $blog_id );
				}

				foreach ( $requests as $request ) {
					$result              = $this->select_one( $request );
					$result['elementId'] = $request['elementId'] ?? null;
					$response[]          = $result;
				}

				if ( get_current_blog_id() !== $blog_id && is_multisite() ) {
					restore_current_blog();
				}
			}

			wp_send_json( $response );
		}

		$response = $this->select_one( $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		wp_send_json( $response );
	}

	/**
	 * Push an Ad Health notice to the queue in the backend
	 *
	 * @return void
	 */
	public function ad_health_notice_push(): void {
		check_ajax_referer( 'advanced-ads-ad-health-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		$key  = ! empty( $_REQUEST['key'] ) ? esc_attr( Params::request( 'key' ) ) : false;
		$attr = Params::request( 'attr', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );

		// Update or new entry?
		if ( isset( $attr['mode'] ) && 'update' === $attr['mode'] ) {
			Advanced_Ads_Ad_Health_Notices::get_instance()->update( $key, $attr );
		} else {
			Advanced_Ads_Ad_Health_Notices::get_instance()->add( $key, $attr );
		}

		die();
	}

	/**
	 * Check if AJAX ad can be displayed, with consent information sent in request.
	 *
	 * @param bool $can_display Whether this ad can be displayed.
	 * @param Ad   $ad          The ad object.
	 *
	 * @return bool
	 */
	public function can_display_by_consent( $can_display, $ad ) {

		// Early bail!!
		if ( ! $can_display ) {
			return $can_display;
		}

		// If consent is overridden for the ad.
		$privacy_props = $ad->get_prop( 'privacy' );
		if ( ! empty( $privacy_props['ignore-consent'] ) ) {
			return true;
		}

		// If privacy module is not active, we can display.
		if ( empty( Advanced_Ads_Privacy::get_instance()->options()['enabled'] ) ) {
			return true;
		}

		$consent_state = Params::request( 'consent', 'not_allowed' );

		// Consent is either given or not needed.
		if ( in_array( $consent_state, [ 'not_needed', 'accepted' ], true ) ) {
			return true;
		}

		if ( 'not_allowed' === $consent_state ) {
			// Allow image ads without custom code.
			if ( $ad->get_type() === 'image' ) {
				$has_custom_code = class_exists( 'Advanced_Ads_Pro' )
					&& ! empty( Advanced_Ads_Pro::get_instance()->get_custom_code( $ad ) );

				if ( ! $has_custom_code ) {
					return true;
				}

				return false;
			}
		}

		// If there is custom code, don't display the ad (unless it's a group).
		if (
			class_exists( 'Advanced_Ads_Pro' ) &&
			! empty( Advanced_Ads_Pro::get_instance()->get_custom_code( $ad ) ) &&
			! $ad->is_type( 'group' )
		) {
			return false;
		}

		if ( $can_display && $ad->get_type() === 'adsense' ) {
			return true;
		}

		// See if this ad type needs consent.
		return ! Advanced_Ads_Privacy::get_instance()->ad_type_needs_consent( $ad->get_type() );
	}

	/**
	 * Subscribe to multiple newsletters
	 */
	public function multiple_subscribe() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		$groups = ! empty( Params::post( 'groups' ) ) ? json_decode( Params::post( 'groups' ), true ) : [];

		if ( ! Conditional::user_can( 'advanced_ads_see_interface' ) || empty( $groups ) ) {
			wp_send_json_error(
				[
					/* translators: %s is a URL. */
					'message' => sprintf( __( 'An error occurred. Please use <a href="%s" target="_blank">this form</a> to sign up.', 'advanced-ads' ), 'http://eepurl.com/bk4z4P' ),
				],
				400
			);
		}

		foreach ( $groups as $group ) {
			$message = Advanced_Ads_Admin_Notices::get_instance()->subscribe( $group );
		}

		wp_send_json_success( [ 'message' => $message ?? '' ] );
	}

	/**
	 * Provides a single ad (ad, group, placement) given ID and selection method.
	 *
	 * @param array $request Request.
	 *
	 * @return array
	 */
	private function select_one( $request ) {
		$method = (string) $request['ad_method'] ?? null;
		if ( 'id' === $method ) {
			$method = 'ad';
		}

		// Early bail!!
		if ( ! Conditional::is_entity_allowed( $method ) ) {
			return [
				'status'  => 'error',
				'message' => __( 'The method is not allowed to render.', 'advanced-ads' ),
			];
		}

		$function  = "get_the_$method";
		$id        = (string) $request['ad_id'] ?? null;
		$arguments = $request['ad_args'] ?? [];

		if ( is_string( $arguments ) ) {
			$arguments = stripslashes( $arguments );
			$arguments = json_decode( $arguments, true );
		}

		if ( ! empty( $request['elementId'] ) ) {
			$arguments['cache_busting_elementid'] = $request['elementId'];
		}

		// Report error.
		if ( empty( $id ) || ! function_exists( $function ) ) {
			return [
				'status'  => 'error',
				'message' => 'No valid ID or METHOD found.',
			];
		}

		/**
		 * Filters the received arguments before passing them to ads/groups/placements.
		 *
		 * @param array $arguments Existing arguments.
		 * @param array $request   Request data.
		 */
		$arguments    = apply_filters( 'advanced-ads-ajax-ad-select-arguments', $arguments, $request );
		$previous_ads = Stats::get()->entities;
		add_filter( 'advanced-ads-can-display-ad', [ $this, 'can_display_by_consent' ], 10, 2 );
		$content = $function( (int) $id, '', $arguments );

		if ( empty( $content ) ) {
			return [
				'status'  => 'error',
				'message' => 'No displayable ad found for privacy settings.',
			];
		}

		$response = [
			'status'  => 'success',
			'item'    => $content,
			'id'      => $id,
			'method'  => $method,
			'ads'     => array_slice( Stats::get()->entities, count( $previous_ads ) ),
			'blog_id' => get_current_blog_id(),
		];

		return apply_filters(
			'advanced-ads-cache-busting-item',
			$response,
			[
				'id'     => $id,
				'method' => $method,
				'args'   => $arguments,
			]
		);
	}

	/**
	 * Load content of the ad parameter metabox
	 *
	 * @since 1.0.0
	 */
	public function load_ad_parameters_metabox() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		$type_string = Params::post( 'ad_type' );
		$ad_id       = Params::post( 'ad_id', 0, FILTER_VALIDATE_INT );
		if ( empty( $ad_id ) ) {
			die();
		}

		if ( wp_advads_has_ad_type( $type_string ) ) {
			$ad      = wp_advads_get_ad( $ad_id, $type_string );
			$ad_type = wp_advads_get_ad_type( $type_string );
			if ( method_exists( $ad_type, 'render_parameters' ) ) {
				$ad_type->render_parameters( $ad );
			}

			if ( $ad_type->has_size() ) {
				include ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-parameters-size.php';
			}

			// Extend the AJAX-loaded parameters form by ad type.
			do_action( "advanced-ads-ad-params-after-{$ad->get_type()}", $ad );
		}

		die();
	}

	/**
	 * Load interface for single visitor condition
	 *
	 * @since 1.5.4
	 */
	public function load_visitor_condition() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		// get visitor condition types.
		$visitor_conditions = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;
		$condition          = [];
		$condition['type']  = Params::post( 'type', '' );
		$index              = Params::post( 'index', 0, FILTER_VALIDATE_INT );

		$form_name = Params::post( 'form_name', Advanced_Ads_Visitor_Conditions::FORM_NAME );

		if ( ! isset( $visitor_conditions[ $condition['type'] ] ) ) {
			die();
		}

		$metabox = $visitor_conditions[ $condition['type'] ]['metabox'];
		if ( method_exists( $metabox[0], $metabox[1] ) ) {
			call_user_func( [ $metabox[0], $metabox[1] ], $condition, $index, $form_name );
		}

		die();
	}

	/**
	 * Load interface for single display condition
	 *
	 * @since 1.7
	 */
	public function load_display_condition() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		// get display condition types.
		$conditions        = Advanced_Ads_Display_Conditions::get_instance()->conditions;
		$condition         = [];
		$condition['type'] = Params::post( 'type', '' );
		$index             = Params::post( 'index', 0, FILTER_VALIDATE_INT );
		$form_name         = Params::post( 'form_name', Advanced_Ads_Display_Conditions::FORM_NAME );

		if ( ! isset( $conditions[ $condition['type'] ] ) ) {
			die();
		}

		$metabox = $conditions[ $condition['type'] ]['metabox'];
		if ( method_exists( $metabox[0], $metabox[1] ) ) {
			call_user_func( [ $metabox[0], $metabox[1] ], $condition, $index, $form_name );
		}

		die();
	}

	/**
	 * Search terms belonging to a specific taxonomy
	 *
	 * @since 1.4.7
	 */
	public function search_terms() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		$args = [
			'taxonomy'   => Params::post( 'tax', '' ),
			'hide_empty' => false,
			'number'     => 20,
		];

		$search = Params::post( 'search', '' );
		if ( '' === $search ) {
			die();
		}

		// if search is an id, search for the term id, else do a full text search.
		if ( 0 !== absint( $search ) && strlen( $search ) === strlen( absint( $search ) ) ) {
			$args['include'] = [ absint( $search ) ];
		} else {
			$args['search'] = $search;
		}

		$results = get_terms( $args );
		echo wp_json_encode( $results );
		echo "\n";
		die();
	}

	/**
	 * Search authors
	 *
	 * @since 1.47.5
	 */
	public function search_authors() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		$args['search_columns'] = [ 'ID', 'user_login', 'user_nicename', 'display_name' ];

		if ( version_compare( get_bloginfo( 'version' ), '5.9' ) > -1 ) {
			$args['capability'] = [ 'edit_posts' ];
		} else {
			$args['who'] = 'authors';
		}

		$search = Params::post( 'search', '' );
		if ( '' === $search ) {
			die();
		}

		$args['search'] = '*' . sanitize_text_field( wp_unslash( $search ) ) . '*';

		$results = get_users( $args );

		echo wp_json_encode( $results );
		die();
	}

	/**
	 * Close a notice for good
	 *
	 * @since 1.5.3
	 */
	public function close_notice() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
		$notice = Params::request( 'notice' );

		if (
			! Conditional::user_can( 'advanced_ads_manage_options' )
			|| empty( $notice )
		) {
			die();
		}

		Advanced_Ads_Admin_Notices::get_instance()->remove_from_queue( $notice );

		// permanent dismissed.
		if ( 'monetize_wizard' === Params::request( 'notice' ) ) {
			update_user_meta( get_current_user_id(), Constants::USER_WIZARD_DISMISS, true );
		}

		$redirect = Params::request( 'redirect' );
		if ( $redirect && wp_safe_redirect( $redirect ) ) {
			exit();
		}

		die();
	}

	/**
	 * Hide a notice for some time (7 days right now)
	 *
	 * @since 1.8.17
	 */
	public function hide_notice() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
		$notice = Params::request( 'notice' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' )
		|| empty( $notice )
		) {
			die();
		}

		Advanced_Ads_Admin_Notices::get_instance()->hide_notice( $notice );
		die();
	}

	/**
	 * Subscribe to newsletter
	 *
	 * @since 1.5.3
	 */
	public function subscribe() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
		$notice = Params::request( 'notice' );

		if (
			! Conditional::user_can( 'advanced_ads_see_interface' )
			|| empty( $notice )
		) {
			wp_send_json_error(
				[
					/* translators: %s is a URL. */
					'message' => sprintf( __( 'An error occurred. Please use <a href="%s" target="_blank">this form</a> to sign up.', 'advanced-ads' ), 'http://eepurl.com/bk4z4P' ),
				],
				400
			);
		}

		wp_send_json_success( [ 'message' => Advanced_Ads_Admin_Notices::get_instance()->subscribe( $notice ) ] );
	}

	/**
	 * Activate license of an add-on
	 *
	 * @since 1.5.7
	 */
	public function activate_license() {
		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		check_ajax_referer( 'advads_ajax_license_nonce', 'security' );

		$addon = Params::post( 'addon' );
		if ( '' === $addon ) {
			die();
		}

		// phpcs:disable
		echo Advanced_Ads_Admin_Licenses::get_instance()->activate_license(
			$addon,
			Params::post( 'pluginname' ),
			Params::post( 'optionslug' ),
			Params::post( 'license' )
		);
		// phpcs:enable

		die();
	}

	/**
	 * Deactivate license of an add-on
	 *
	 * @since 1.6.11
	 */
	public function deactivate_license() {
		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		check_ajax_referer( 'advads_ajax_license_nonce', 'security' );

		$addon = Params::post( 'addon' );
		if ( '' === $addon ) {
			die();
		}

		// phpcs:disable
		echo Advanced_Ads_Admin_Licenses::get_instance()->deactivate_license(
			$addon,
			Params::post( 'pluginname' ),
			Params::post( 'optionslug' )
		);
		// phpcs:enable

		die();
	}

	/**
	 * Rebuild assets for ad-blocker module
	 */
	public function adblock_rebuild_assets() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		Advanced_Ads_Ad_Blocker_Admin::get_instance()->add_asset_rebuild_form();
		die();
	}

	/**
	 * Post search (used in Display conditions)
	 */
	public function post_search() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		add_filter( 'wp_link_query_args', [ 'Advanced_Ads_Display_Conditions', 'modify_post_search' ] );
		add_filter( 'posts_search', [ 'Advanced_Ads_Display_Conditions', 'modify_post_search_sql' ] );

		wp_ajax_wp_link_ajax();
	}

	/**
	 * Inject an ad and a placement
	 *
	 * @since 1.7.3
	 */
	public function inject_placement() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		$ad_id = Params::request( 'ad_id', 0, FILTER_VALIDATE_INT );

		// Early bail!!
		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) || ! $ad_id ) {
			die();
		}

		// use existing placement.
		$placement_id = Params::request( 'placement_id', 0, FILTER_VALIDATE_INT );
		if ( $placement_id ) {
			$placement = wp_advads_get_placement( $placement_id );

			if ( $placement ) {
				$current_item = $placement->get_item();
				// Check if current item is a group and new item is an ad.
				if ( is_string( $current_item ) && strpos( $current_item, 'group_' ) === 0 ) {
					$group = wp_advads_get_group( (int) str_replace( 'group_', '', $current_item ) );
					if ( $group ) {
						$ad_weights           = $group->get_ad_weights();
						$ad_weights[ $ad_id ] = Constants::GROUP_AD_DEFAULT_WEIGHT;
						$group->set_ad_weights( $ad_weights );
						$group->save();
					}
				} else {
					$placement->set_item( 'ad_' . $ad_id );
					$placement->save();
				}
				echo esc_attr( $placement_id );
			}

			die();
		}

		$type = esc_attr( Params::request( 'placement_type' ) );
		if ( ! wp_advads_has_placement_type( $type ) ) {
			die();
		}

		$new_placement = wp_advads_create_new_placement( $type );

		$props = [
			'item'  => 'ad_' . $ad_id,
			'title' => wp_advads_get_placement_type( $type )->get_title(),
		];

		// set content specific options.
		if ( $new_placement->is_type( 'post_content' ) ) {
			$options           = Params::request( 'options', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
			$index             = (int) Arr::get( $options, 'index', 1 );
			$props['position'] = 'after';
			$props['index']    = $index;
			$props['tag']      = 'p';
		}

		$new_placement->set_props( $props );
		echo $new_placement->save();; // phpcs:ignore
	}

	/**
	 * Save ad wizard state for each user individually
	 *
	 * @since 1.7.4
	 */
	public function save_wizard_state() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			return;
		}

		$user_id = get_current_user_id();

		if ( ! $user_id ) {
			die();
		}

		$state = 'true' === Params::request( 'hideWizard' ) ? 'true' : 'false';
		update_user_meta( $user_id, 'advanced-ads-hide-wizard', $state );

		die();
	}

	/**
	 * Enable Adsense Auto ads, previously "Page-Level ads"
	 */
	public function adsense_enable_pla() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		$options                       = get_option( GADSENSE_OPT_NAME, [] );
		$options['page-level-enabled'] = true;
		update_option( GADSENSE_OPT_NAME, $options );
		die();
	}

	/**
	 * Display list of Ad Health notices
	 */
	public function ad_health_notice_display() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		Advanced_Ads_Ad_Health_Notices::get_instance()->render_widget();
		die();
	}

	/**
	 * Push an Ad Health notice to the queue
	 */
	public function ad_health_notice_push_adminui() {

		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		$key  = Params::request( 'key' );
		$attr = Params::request( 'attr', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
		$attr = ! empty( $attr ) && is_array( $attr ) ? $attr : [];

		// update or new entry?
		if ( isset( $attr['mode'] ) && 'update' === $attr['mode'] ) {
			Advanced_Ads_Ad_Health_Notices::get_instance()->update( $key, $attr );
		} else {
			Advanced_Ads_Ad_Health_Notices::get_instance()->add( $key, $attr );
		}

		die();
	}

	/**
	 * Hide Ad Health notice
	 */
	public function ad_health_notice_hide() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		$notice     = Params::request( 'notice', '' );
		$notice_key = ! empty( $notice ) ? esc_attr( $notice ) : false;

		Advanced_Ads_Ad_Health_Notices::get_instance()->hide( $notice_key );
		die();
	}

	/**
	 * Show all ignored notices of a given type
	 */
	public function ad_health_notice_unignore() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
			return;
		}

		Advanced_Ads_Ad_Health_Notices::get_instance()->unignore();
		die();
	}

	/**
	 * After the user has selected a new frontend element, update the corresponding placement.
	 */
	public function update_frontend_element() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		if ( ! Conditional::user_can( 'advanced_ads_manage_placements' ) ) {
			return;
		}

		$return = wp_update_post( $_POST );

		if ( is_wp_error( $return ) ) {
			wp_send_json_error( [ 'error' => $return->get_error_message() ], 400 );
		}

		wp_send_json_success( [ 'id' => $return ] );
	}

	/**
	 * Get hints related to the Gutenberg block.
	 */
	public function get_block_hints() {
		check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );

		$item = Params::post( 'itemID' );
		if ( ! $item || ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
			die;
		}

		$item = explode( '_', $item );
		if ( ! isset( $item[0] ) || 'group' !== $item[0] ) {
			die;
		}

		$group = wp_advads_get_group( absint( $item[1] ) );
		if ( ! $group ) {
			die;
		}

		wp_send_json_success( $group->get_hints() );
	}
}