: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
public function save_post_meta_box( $post_id ) {
if ( ! WordPress::user_can( 'advanced_ads_edit_ads' ) ) {
if ( ! isset( $_POST['advads_post_meta_box_nonce'] ) ) {
$nonce = $_POST['advads_post_meta_box_nonce'];
// Verify that the nonce is valid.
if ( ! wp_verify_nonce( $nonce, 'advads_post_meta_box' ) ) {
// don’t save on autosave.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
// check the user's permissions.
if ( 'page' === $_POST['post_type'] ) {
if ( ! current_user_can( 'edit_page', $post_id ) ) {
if ( ! current_user_can( 'edit_post', $post_id ) ) {
// sanitize the user input.
$_data['disable_ads'] = isset( $_POST['advanced_ads']['disable_ads'] ) ? absint( $_POST['advanced_ads']['disable_ads'] ) : 0;
$_data = apply_filters( 'advanced_ads_save_post_meta_box', $_data );
// update the meta field.
update_post_meta( $post_id, '_advads_ad_settings', $_data );
* Add "close" class to collapse the ad-type metabox after ad was saved first
* @param array $classes class attributes.
public function close_ad_type_metabox( $classes = [] ) {
if ( isset( $post->ID ) && 'publish' === $post->post_status ) {
if ( ! in_array( 'closed', $classes, true ) ) {
* Add dashboard widget with ad stats and additional information
public function add_dashboard_widget() {
// display dashboard widget only to authors and higher roles.
if ( ! WordPress::user_can( 'advanced_ads_see_interface' ) ) {
add_meta_box( 'advads_dashboard_widget', __( 'Advanced Ads', 'advanced-ads' ), [ $this, 'dashboard_widget_function' ], 'dashboard', 'side', 'high' );
* Display widget functions
* @param WP_Post $post post object.
* @param array $callback_args callback arguments.
public static function dashboard_widget_function( $post, $callback_args ) {
$ads_count = Advanced_Ads::get_number_of_ads();
if ( WordPress::user_can( 'advanced_ads_edit_ads' ) ) {
// translators: %1$d is the number of ads, %2$s and %3$s are URLs.
wp_kses( __( '%1$d ads – <a href="%2$s">manage</a> - <a href="%3$s">new</a>', 'advanced-ads' ), [ 'a' => [ 'href' => [] ] ] ),
'edit.php?post_type=' . esc_attr( Entities::POST_TYPE_AD ),
'post-new.php?post_type=' . esc_attr( Entities::POST_TYPE_AD )
$notice_options = Advanced_Ads_Admin_Notices::get_instance()->options();
$_notice = 'nl_first_steps';
if ( ! isset( $notice_options['closed'][ $_notice ] ) ) {
<div class="advads-admin-notice">
<p><button type="button" class="button-primary advads-notices-button-subscribe" data-notice="<?php echo esc_attr( $_notice ); ?>"><?php esc_html_e( 'Get the tutorial via email', 'advanced-ads' ); ?></button></p>
if ( ! isset( $notice_options['closed'][ $_notice ] ) ) {
<div class="advads-admin-notice">
<p><button type="button" class="button-primary advads-notices-button-subscribe" data-notice="<?php echo esc_attr( $_notice ); ?>"><?php esc_html_e( 'Get AdSense tips via email', 'advanced-ads' ); ?></button></p>
self::dashboard_cached_rss_widget();
<p><a href="https://wpadvancedads.com/category/tutorials/?utm_source=advanced-ads&utm_medium=link&utm_campaign=dashboard" target="_blank"><?php esc_html_e( 'Visit our blog for more articles about ad optimization', 'advanced-ads' ); ?></a></p>
// add markup for utm variables.
// todo: move to js file.
<script>jQuery('#advads_dashboard_widget .rss-widget a').each(function(){ this.href = this.href + '?utm_source=advanced-ads&utm_medium=rss-link&utm_campaign=dashboard'; })</script>
* Checks to see if there are feed urls in transient cache; if not, load them
* built using a lot of https://developer.wordpress.org/reference/functions/wp_dashboard_cached_rss_widget/
* @return bool False on failure. True on success.
public static function dashboard_cached_rss_widget() {
$cache_key = 'dash_' . md5( 'advads_dashboard_widget' );
$output = get_transient( $cache_key );
if ( false !== ( $output ) ) {
echo $output; // complex HTML widget.
* Only display dummy output which then loads the content via AJAX
<div id="advads-dashboard-widget-placeholder">
<img src="<?php echo esc_url( admin_url( 'images/spinner.gif' ) ); ?>" width="20" height="20" alt="spinner"/>
<script>window.addEventListener( 'load', function() { advads_load_dashboard_rss_widget_content() } );</script>
* Create the rss output of the widget
public static function dashboard_widget_function_output() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$cache_key = 'dash_' . md5( 'advads_dashboard_widget' );
'link' => 'https://wpadvancedads.com/',
'url' => 'https://wpadvancedads.com/category/tutorials/feed/',
// translators: %s is our URL.
__( 'Latest posts on wpadvancedads.com', 'advanced-ads' ),
'https://wpadvancedads.com/'
// create output and also cache it.
foreach ( $feeds as $_feed ) {
echo '<div class="rss-widget">';
echo '<h4>' . esc_html( $_feed['title'] ) . '</h4>';
wp_widget_rss_output( $_feed['url'], $_feed );
$feed_content = ob_get_clean();
$error_string = '<strong>' . __( 'RSS Error:' ) . '</strong> ';
// empty the widget content, if we find the error string in it.
if ( strpos( $feed_content, $error_string ) ) {
set_transient( $cache_key, $feed_content, 48 * HOUR_IN_SECONDS ); // Default lifetime in cache of 48 hours.
* Fixes a WP QUADS PRO compatibility issue
* they inject their ad optimization meta box into our ad page, even though it is not a public post type
* using they filter, we remove AA from the list of post types they inject this box into
* @param array $allowed_post_types array of allowed post types.
public function fix_wpquadspro_issue( $allowed_post_types ) {
unset( $allowed_post_types['advanced_ads'] );
return $allowed_post_types;
* Render meta box for ad settings notice when ads disabled for post type
* @param WP_Post $post The post object.
public function render_disable_post_type_notice( $post ) {
$labels = get_post_type_object( $post->post_type )->labels;
include ADVADS_ABSPATH . 'admin/views/post-ad-settings-hint-metabox.php';