Files
roi-theme/wp-content/plugins/advanced-ads/classes/ad-health-notices.php
root a22573bf0b Commit inicial - WordPress Análisis de Precios Unitarios
- 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>
2025-11-03 21:04:30 -06:00

838 lines
23 KiB
PHP
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php // phpcs:ignoreFile
use AdvancedAds\Framework\Utilities\Arr;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Utilities\WordPress;
/**
* Container class for Ad Health notice handling
*
* @package WordPress
* @subpackage Advanced Ads Plugin
* @since 1.12
*
* related scripts / functions
*
* advads_push_notice() function to push notifications using AJAX in admin/assets/js/admin-global.js
* push_ad_health_notice() in AdvancedAds\Admin\Ajax to push notifications sent via AJAX
* Advanced_Ads_Checks  for the various checks
* list of notification texts in admin/includes/ad-health-notices.php
*/
class Advanced_Ads_Ad_Health_Notices {
/**
* Options
*
* @var array
*/
protected $options;
/**
* All detected notices
*
* Structure is
* [notice_key] => array(
* 'text' - if not given, it uses the default text for output )
* 'orig_key' - original notice key
* )
*
* @var array
*/
public $notices = [];
/**
* All ignored notices
*
* @var array
*/
public $ignore = [];
/**
* All displayed notices ($notices minus $hidden)
*
* @var array
*/
public $displayed_notices = [];
/**
* Load default notices
*
* @var array
*/
public $default_notices = [];
/**
* The last notice key saved
*
* @var string
*/
public $last_saved_notice_key = false;
/**
* Name of the transient saved for daily checks in the backend
*
* @const string
*/
const DAILY_CHECK_TRANSIENT_NAME = 'advanced-ads-daily-ad-health-check-ran';
/**
* Return an instance of this class.
*
* @return object A single instance of this class.
*/
public static function get_instance() {
static $instance;
// If the single instance hasn't been set, set it now.
if ( null === $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Advanced_Ads_Ad_Health_Notices constructor.
*/
public function __construct() {
// failsafe for there were some reports of 502 errors.
if ( 1 < did_action( 'plugins_loaded' ) ) {
return;
}
// stop here if notices are disabled.
if ( ! self::notices_enabled() ) {
return;
}
add_action( 'init', [ $this, 'load_default_notices' ] );
add_action( 'init', [ $this, 'load_notices' ] );
/**
* Run checks
* needs to run after plugins_loaded with priority 10
* current_screen seems like the perfect hook
*/
add_action( 'current_screen', [ $this, 'run_checks' ], 20 );
// add notification when an ad expires.
add_action( 'advanced-ads-ad-expired', [ $this, 'ad_expired' ] );
}
/**
* Check if notices are enabled using "disable-notices" option in plugin settings
*
* @return bool
*/
public static function notices_enabled() {
$options = Advanced_Ads::get_instance()->options();
return empty( $options['disable-notices'] );
}
public function load_default_notices() {
// load default notices.
if ( [] === $this->default_notices ) {
include ADVADS_ABSPATH . '/admin/includes/ad-health-notices.php';
$this->default_notices = $advanced_ads_ad_health_notices;
}
}
/**
* Load notice arrays
*/
public function load_notices() {
$options = $this->options();
// load notices from "notices".
$this->notices = $options['notices'] ?? [];
/**
* Cleanup notices
*/
foreach ( $this->notices as $_key => $_notice ) {
// without valid key caused by an issue prior to 1.13.3.
if ( empty( $_key ) ) {
unset( $this->notices[ $_key ] );
}
$time = current_time( 'timestamp', 0 );
$notice_array = $this->get_notice_array_for_key( $_key );
// handle notices with a timeout.
if ( isset( $_notice['closed'] ) ) {
// remove notice when timeout expired was closed longer ago than timeout set in the notice options.
if ( empty( $notice_array['timeout'] )
|| ( ( $time - $_notice['closed'] ) > $notice_array['timeout'] ) ) {
$this->remove( $_key );
} else {
// just ignore notice if timeout is still valid.
unset( $this->notices[ $_key ] );
}
}
// check if notice still exists.
if ( [] === $this->get_notice_array_for_key( $_key ) ) {
unset( $this->notices[ $_key ] );
}
}
// unignore notices if `show-hidden=true` is set in the URL.
$nonce = Params::get( 'advads_nonce' );
if (
$nonce && wp_verify_nonce( wp_unslash( $nonce ), 'advanced-ads-show-hidden-notices' )
&& true === Params::get( 'advads-show-hidden-notices', false, FILTER_VALIDATE_BOOLEAN )
) {
$this->unignore();
// remove the argument from the URL.
add_filter( 'removable_query_args', [ $this, 'remove_query_vars_after_notice_update' ] );
}
// load hidden notices.
$this->ignore = $this->get_valid_ignored();
// get displayed notices
// get keys of notices.
$notice_keys = array_keys( $this->notices );
$this->displayed_notices = array_diff( $notice_keys, $this->ignore );
}
/**
* Remove query var from URL after notice was updated
*
* @param array $removable_query_args array with removable query vars.
* @return array updated query vars.
*/
public function remove_query_vars_after_notice_update( $removable_query_args ) {
$removable_query_args[] = 'advads-show-hidden-notices';
$removable_query_args[] = 'advads_nonce';
return $removable_query_args;
}
/**
* Manage when to run checks
* - only when users have ads
* - once per day on any backend page
* - on each Advanced Ads related page
*/
public function run_checks() {
// run in WP Admin only and if there are any ads.
if ( ! is_admin() || ! WordPress::get_count_ads() ) {
return;
}
// dont run on AJAX calls.
if ( wp_doing_ajax() ) {
return;
}
// run only daily unless we are on an Advanced Ads related page.
if ( ! Conditional::is_screen_advanced_ads()
&& get_transient( self::DAILY_CHECK_TRANSIENT_NAME ) ) {
return;
}
$this->checks();
}
/**
* General checks done on each Advanced Ads-related page or once per day
*/
public function checks() {
$checks = [
'old_php' => ! Advanced_Ads_Checks::php_version_minimum(),
'conflicting_plugins' => count( Advanced_Ads_Checks::conflicting_plugins() ),
'php_extensions_missing' => count( Advanced_Ads_Checks::php_extensions() ),
'ads_disabled' => Advanced_Ads_Checks::ads_disabled(),
'constants_enabled' => Advanced_Ads_Checks::get_defined_constants(),
'assets_expired' => Advanced_Ads_Checks::assets_expired(),
'license_invalid' => Advanced_Ads_Checks::licenses_invalid(),
'buddypress_no_pro' => class_exists( 'BuddyPress', false ) && ! defined( 'BP_PLATFORM_VERSION' ) && ! defined( 'AAP_VERSION' ),
'buddyboss_no_pro' => defined( 'BP_PLATFORM_VERSION' ) && ! defined( 'AAP_VERSION' ),
'gamipress_no_pro' => class_exists( 'GamiPress', false ) && ! defined( 'AAP_VERSION' ),
'pmp_no_pro' => defined( 'PMPRO_VERSION' ) && ! defined( 'AAP_VERSION' ),
'members_no_pro' => function_exists( 'members_plugin' ) && ! defined( 'AAP_VERSION' ),
'translatepress_no_pro' => function_exists( 'trp_enable_translatepress' ) && ! defined( 'AAP_VERSION' ),
'weglot_no_pro' => defined( 'WEGLOT_VERSION' ) && ! defined( 'AAP_VERSION' ),
'learndash' => defined( 'LEARNDASH_VERSION' ),
'aawp' => defined( 'AAWP_PLUGIN_FILE' ),
'polylang' => defined( 'POLYLANG_VERSION' ),
'mailpoet' => function_exists( 'mailpoet_check_requirements' ),
'wp_rocket' => Advanced_Ads_Checks::active_wp_rocket(),
'quiz_plugins_no_pro' => Advanced_Ads_Checks::active_quiz_plugins(),
'elementor' => defined( 'ELEMENTOR_VERSION' ),
'siteorigin' => defined( 'SITEORIGIN_PANELS_VERSION' ),
'divi_no_pro' => function_exists( 'et_setup_theme' ) || defined( 'ET_BUILDER_PLUGIN_VERSION' ),
'beaver_builder' => class_exists( 'FLBuilderLoader' ),
'pagelayer' => defined( 'PAGELAYER_FILE' ),
'wpb' => defined( 'WPB_VC_VERSION' ),
'newspaper' => defined( 'TAGDIV_ROOT' ),
'bbpress_no_pro' => class_exists( 'bbPress', false ) && ! defined( 'AAP_VERSION' ),
'WPML_active' => defined( 'ICL_SITEPRESS_VERSION' ),
'AMP_active' => Advanced_Ads_Checks::active_amp_plugin(),
'wpengine' => Advanced_Ads_Checks::wp_engine_hosting(),// do not remove
'ads_txt_plugins_enabled' => count( Advanced_Ads_Checks::ads_txt_plugins() ),
'header_footer_plugins_enabled' => count( Advanced_Ads_Checks::header_footer_plugins() ),
];
foreach ( $checks as $key => $check ) {
if ( $check ) {
$this->add( $key );
} elseif ( 'wpengine' !== $key ) {
$this->remove( $key );
}
}
set_transient( self::DAILY_CHECK_TRANSIENT_NAME, true, DAY_IN_SECONDS );
}
/**
* Add a notice to the queue
*
* @param string $notice_key notice key to be added to the notice array.
* @param array $atts additional attributes.
*
* attributes
* - append_key string attached to the key; enables to create multiple messages for one original key
* - append_text text added to the default message
* - ad_id ID of an ad, attaches the link to the ad edit page to the message
*/
public function add( $notice_key, $atts = [] ) {
// Early bail!!
if ( empty( $notice_key ) || ! self::notices_enabled() ) {
return;
}
// add string to key.
if ( ! empty( $atts['append_key'] ) ) {
$orig_notice_key = $notice_key;
$notice_key .= $atts['append_key'];
}
$options = $this->options();
$notice_key = sanitize_key( $notice_key );
// load notices from "queue".
$notices = $options['notices'] ?? [];
// check if notice_key was already saved, this prevents the same notice from showing up in different forms.
if ( isset( $notices[ $notice_key ] ) ) {
return;
}
// save the new notice key.
$notices[ $notice_key ] = [];
// save text, if given.
if ( ! empty( $atts['text'] ) ) {
$notices[ $notice_key ]['text'] = $atts['text'];
}
// attach link to ad, if given.
if ( ! empty( $atts['ad_id'] ) ) {
$id = absint( $atts['ad_id'] );
$ad = wp_advads_get_ad( $id );
if ( $id && '' !== $ad->get_title() ) {
$edit_link = ' <a href="' . admin_url( 'post.php?post=' . $id . '&action=edit' ) . '">' . $ad->get_title() . '</a>';
$notices[ $notice_key ]['append_text'] = isset( $notices[ $notice_key ]['append_text'] ) ? $notices[ $notice_key ]['append_text'] . $edit_link : $edit_link;
}
}
// save the original key, if we manipulated it.
if ( ! empty( $atts['append_key'] ) ) {
$notices[ $notice_key ]['orig_key'] = $orig_notice_key;
}
// add more text.
if ( ! empty( $atts['append_text'] ) ) {
$notices[ $notice_key ]['append_text'] = esc_attr( $atts['append_text'] );
}
// add current time we store localized time including the offset set in WP.
$notices[ $notice_key ]['time'] = current_time( 'timestamp', 0 );
$this->last_saved_notice_key = $notice_key;
$this->update_notices( $notices );
}
/**
* Updating an existing notice or add it, if it doesnt exist, yet
*
* @param string $notice_key notice key to be added to the notice array.
* @param array $atts additional attributes.
*
* attributes:
* - append_text  text added to the default message
*/
public function update( $notice_key, $atts = [] ) {
// Early bail!!
if ( empty( $notice_key ) || ! self::notices_enabled() ) {
return;
}
// check if the notice already exists.
$notice_key = esc_attr( $notice_key );
$options = $this->options();
// load notices from "queue".
$notices = isset( $options['notices'] ) ? $options['notices'] : [];
// check if notice_key was already saved, this prevents the same notice from showing up in different forms.
if ( ! isset( $notices[ $notice_key ] ) ) {
$this->add( $notice_key, $atts );
$notice_key = $this->last_saved_notice_key;
// just in case, get notices again.
$notices = $this->notices;
} else {
// add more text if this is an update.
if ( ! empty( $atts['append_text'] ) ) {
$notices[ $notice_key ]['append_text'] = isset( $notices[ $notice_key ]['append_text'] ) ? $notices[ $notice_key ]['append_text'] . $atts['append_text'] : $atts['append_text'];
}
// add `closed` marker, if given.
if ( ! empty( $atts['closed'] ) ) {
$notices[ $notice_key ]['closed'] = absint( $atts['closed'] );
}
}
// update db.
$this->update_notices( $notices );
}
/**
* Decide based on the notice, whether to remove or ignore it
*
* @param string $notice_key key of the notice.
*/
public function hide( $notice_key ) {
if ( empty( $notice_key ) ) {
return;
}
// get original notice array for the "hide" attribute.
$notice_array = $this->get_notice_array_for_key( $notice_key );
// handle notices with a timeout.
// set `closed` timestamp if the notice definition has a timeout information.
if ( isset( $notice_array['timeout'] ) ) {
$this->update( $notice_key, [ 'closed' => current_time( 'timestamp', 0 ) ] );
return;
}
if ( isset( $notice_array['hide'] ) && false === $notice_array['hide'] ) {
// remove item.
$this->remove( $notice_key );
} else {
// hide item.
$this->ignore( $notice_key );
}
}
/**
* Remove notice
* Would remove it from "notice" array. The notice can be added anytime again
* practically, this allows users to "skip" an notice if they are sure that it was only temporary
*
* @param string $notice_key notice key to be removed.
*/
public function remove( $notice_key ) {
// Early bail!!
if ( empty( $notice_key ) || ! self::notices_enabled() ) {
return;
}
$options = $this->options();
if (
! isset( $options['notices'] )
|| ! is_array( $options['notices'] )
|| ! isset( $options['notices'][ $notice_key ] )
) {
return;
}
unset( $options['notices'][ $notice_key ] );
$this->update_notices( $options['notices'] );
}
/**
* Ignore any notice
* adds notice key into "ignore" array
* does not remove it from "notices" array
*
* @param string $notice_key key of the notice to be ignored.
*/
public function ignore( $notice_key ) {
// Early bail!!
if ( empty( $notice_key ) || ! self::notices_enabled() ) {
return;
}
$options = $this->options();
$ignored = isset( $options['ignore'] ) && is_array( $options['ignore'] ) ? $options['ignore'] : [];
// adds notice key to ignore array if it doesnt exist already.
if ( false === array_search( $notice_key, $ignored, true ) ) {
$ignored[] = $notice_key;
}
// update db.
$this->update_ignore( $ignored );
}
/**
* Clear all "ignore" messages
*/
public function unignore() {
$this->update_ignore();
}
/**
* Update ignored notices if there is any change
*
* @param string[] $ignore_list list of ignored keys.
*
* @return void
*/
public function update_ignore( $ignore_list = [] ) {
$options = $this->options();
$before = Arr::get( $options, 'ignore', [] );
if ( $ignore_list === $before ) {
return;
}
$options['ignore'] = $ignore_list;
$this->update_options( $options );
}
/**
* Update notices list if there is any change
*
* @param array $notices New options.
*
* @return void
*/
public function update_notices( $notices ): void {
$options = $this->options();
if ( Arr::get( $options, 'notices', [] ) === $notices ) {
return;
}
$options['notices'] = $notices;
$this->update_options( $options );
$this->load_notices();
}
/**
* Render notice widget on overview page
*/
public function render_widget() {
$ignored_count = count( $this->ignore );
include ADVADS_ABSPATH . 'views/admin/widgets/aa-dashboard/overview-notices.php';
}
/**
* Display notices in a list
*
* @param string $type which type of notice to show; default: 'problem'.
*
* @return void
*/
public function display( $type = 'problem' ) {
// Early baill!!
if ( ! is_array( $this->notices ) ) {
return;
}
foreach ( $this->notices as $_notice_key => $_notice ) {
$notice_array = $this->get_notice_array_for_key( $_notice_key );
// remove the notice if key doesnt exist anymore.
if ( [] === $notice_array ) {
$this->remove( $_notice_key );
}
$notice_type = isset( $notice_array['type'] ) ? $notice_array['type'] : 'problem';
// skip if type is not correct.
if ( $notice_type !== $type ) {
continue;
}
if ( ! empty( $_notice['text'] ) ) {
$text = $_notice['text'];
} elseif ( isset( $notice_array['text'] ) ) {
$text = $notice_array['text'];
} else {
continue;
}
// attach "append_text".
if ( ! empty( $_notice['append_text'] ) ) {
$text .= $_notice['append_text'];
}
// attach "get help" link.
if ( ! empty( $_notice['get_help_link'] ) ) {
$text .= $this->get_help_link( $_notice['get_help_link'] );
} elseif ( isset( $notice_array['get_help_link'] ) ) {
$text .= $this->get_help_link( $notice_array['get_help_link'] );
}
$can_hide = ( ! isset( $notice_array['can_hide'] ) || true === $notice_array['can_hide'] ) ? true : false;
$hide = ( ! isset( $notice_array['hide'] ) || true === $notice_array['hide'] ) ? true : false;
$is_hidden = in_array( $_notice_key, $this->ignore, true ) ? true : false;
$date = isset( $_notice['time'] ) ? date_i18n( get_option( 'date_format' ), $_notice['time'] ) : false;
$dashicon = 'dashicons-warning';
if ( 'notice' === $type ) {
$dashicon = 'dashicons-info';
} elseif ( 'pitch' === $type ) {
$dashicon = 'dashicons-lightbulb';
}
include ADVADS_ABSPATH . '/admin/views/overview-notice-row.php';
}
}
/**
* Display plugins and themes pitches
*
* @return void
*/
public function display_pitches() {
$this->display( 'pitch' );
}
/**
* Display problems.
*/
public function display_problems() {
$this->display( 'problem' );
}
/**
* Display notices.
*/
public function display_notices() {
$this->display( 'notice' );
}
/**
* Return notices option from DB
*
* @return array $options
*/
public function options() {
if ( ! isset( $this->options ) ) {
$this->options = get_option( ADVADS_SLUG . '-ad-health-notices', [] );
}
if ( ! is_array( $this->options ) ) {
$this->options = [];
}
return $this->options;
}
/**
* Update notice options
*
* @param array $options new options.
*/
public function update_options( array $options ) {
// do not allow to clear options.
if ( [] === $options ) {
return;
}
$this->options = $options;
update_option( ADVADS_SLUG . '-ad-health-notices', $options );
}
/**
* Get the number of overall visible notices
*/
public static function get_number_of_notices() {
$displayed_notices = self::get_instance()->displayed_notices;
if ( ! is_array( $displayed_notices ) ) {
return 0;
}
return count( $displayed_notices );
}
/**
* Get ignored messages that are also in the notices
* also updates ignored array, if needed
*/
public function get_valid_ignored() {
$options = $this->options();
$ignore_before = $options['ignore'] ?? [];
// get keys from notices.
$notice_keys = array_keys( $this->notices );
// get the errors that are in ignore AND notices and reset the keys.
$ignore = array_values( array_intersect( $ignore_before, $notice_keys ) );
// only update if changed.
if ( $ignore !== $ignore_before ) {
$this->update_ignore( $ignore );
}
return $ignore;
}
/**
* Check if there are visible problems (notices of type "problem")
*
* @return bool true if there are visible notices (notices that are not hidden)
*/
public static function has_visible_problems() {
$displayed_notices = self::get_instance()->displayed_notices;
if ( ! is_array( $displayed_notices ) ) {
return false;
}
return 0 < count( $displayed_notices );
}
/**
* Get visible notices by type hidden and displayed
*
* @param string $type type of the notice.
*
* @return array
*/
public function get_visible_notices_by_type( $type = 'problem' ) {
$notices_by_type = [];
foreach ( $this->notices as $_key => $_notice ) {
$notice_array = $this->get_notice_array_for_key( $_key );
if ( isset( $notice_array['type'] ) && $type === $notice_array['type']
&& ( ! isset( $this->ignore ) || false === array_search( $_key, $this->ignore, true ) ) ) {
$notices_by_type[ $_key ] = $_notice;
}
}
return $notices_by_type;
}
/**
* Check if there are notices
*
* @return bool true if there are notices, false if not
*/
public function has_notices() {
return isset( $this->notices ) && is_array( $this->notices ) && count( $this->notices );
}
/**
* Check if there are visible notices for a given type
*
* @param string $type type of the notice.
*
* @return integer
*/
public function has_notices_by_type( $type = 'problem' ) {
$notices = $this->get_visible_notices_by_type( $type );
if ( ! is_array( $notices ) ) {
return 0;
}
return count( $notices );
}
/**
* Get the notice array for a notice key
* useful, if a notice key was manipulated
*
* @param string $notice_key key of the notice.
*
* @return array type
*/
public function get_notice_array_for_key( $notice_key ) {
// check if there is an original key.
$orig_key = isset( $this->notices[ $notice_key ]['orig_key'] ) ? $this->notices[ $notice_key ]['orig_key'] : $notice_key;
return isset( $this->default_notices[ $orig_key ] ) ? $this->default_notices[ $orig_key ] : [];
}
/**
* Add notification when an ad expires based on the expiry date
*
* @param integer $ad_id ID of the ad.
*
* @return void
*/
public function ad_expired( $ad_id ): void {
$id = ! empty( $ad_id ) ? absint( $ad_id ) : 0;
$this->update(
'ad_expired',
[
'append_key' => $id,
'ad_id' => $id,
]
);
}
/**
* Get AdSense error link
* this is a copy of Advanced_Ads_AdSense_MAPI::get_adsense_error_link() which might not be available all the time
*
* @param string $code error code.
*
* @return string link
*/
public static function get_adsense_error_link( $code ) {
if ( ! empty( $code ) ) {
$code = '-' . $code;
}
if ( class_exists( 'Advanced_Ads_AdSense_MAPI', false ) ) {
return Advanced_Ads_AdSense_MAPI::get_adsense_error_link( 'disapprovedAccount' );
}
// is a copy of Advanced_Ads_AdSense_MAPI::get_adsense_error_link().
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>'
);
}
/**
* Return a "Get Help" link
*
* @param string $link target URL.
*
* @return string HTML of the target link
*/
public function get_help_link( $link ) {
$link = esc_url( $link );
if ( ! $link ) {
return '';
}
return '&nbsp;<a href="' . $link . '" target="_blank">' . __( 'Get help', 'advanced.ads' ) . '</a>';
}
}