Files
roi-theme/wp-content/plugins/advanced-ads/public/class-advanced-ads.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

803 lines
22 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
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
/**
* Advanced Ads.
*
* @package AdvancedAds
* @author Thomas Maier <support@wpadvancedads.com>
* @license GPL-2.0+
* @link https://wpadvancedads.com
* @copyright 2013-2018 Thomas Maier, Advanced Ads GmbH
*/
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Abstracts\Group;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Installation\Capabilities;
/**
* Plugin class. This class should ideally be used to work with the
* public-facing side of the WordPress site.
*
* @package Advanced_Ads
* @author Thomas Maier <support@wpadvancedads.com>
*/
class Advanced_Ads {
/**
* Instance of this class.
*
* @var object
*/
private static $instance = null;
/**
* Ad types
*
* @deprecated 1.48.2
*
* @var array Ad types
*/
public $ad_types = [];
/**
* Plugin options
*
* @var array $options
*/
protected $options;
/**
* Interal plugin options set by the plugin
*
* @var array $internal_options
*/
protected $internal_options;
/**
* Adblocker Plugin options
*
* @var array (if loaded)
*/
protected $adblocker_options = null;
/**
* Whether the loop started in an inner `the_content`.
*
* @var bool
*/
protected $was_in_the_loop = false;
/**
* Signifies Whether the loop has started and the caller is in the loop.
*
* We need it because Some the "Divi" theme calls the `loop_start/loop_end` hooks
* instead of calling `the_post()` to signify that the caller is in the loop.
*
* @var bool
*/
protected $in_the_loop = false;
/**
* Is the query the main query?, when WP_Query is used
*
* @var bool
*/
private $is_main_query;
/**
* Save number of ads
*
* @var array
*/
private $number_of_ads = [];
/**
* Initialize frontend features
*/
private function __construct() {
add_action( 'plugins_loaded', [ $this, 'wp_plugins_loaded' ] );
// allow add-ons to interact.
add_action( 'init', [ $this, 'advanced_ads_loaded' ], 9 );
add_filter( 'the_content', [ $this, 'set_was_in_the_loop' ], ~PHP_INT_MAX );
}
/**
* Return an instance of this class.
*
* @return Advanced_Ads A single instance of this class.
*/
public static function get_instance() {
// if the single instance hasn't been set, set it now.
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Initialize the plugin by setting localization and loading public scripts
* and styles.
*/
public function wp_plugins_loaded() {
// register hooks and filters for auto ad injection.
$this->init_injection();
// add meta robots noindex, nofollow to images, which are part of 'Image ad' ad type.
add_action( 'wp_head', [ $this, 'noindex_attachment_images' ] );
// use custom CSS or other custom header code.
add_action( 'wp_head', [ $this, 'custom_header_code' ] );
// check if ads are disabled in secondary queries.
add_action( 'the_post', [ $this, 'set_query_type' ], 10, 2 );
add_action( 'loop_start', [ $this, 'set_loop_start' ], 10, 0 );
add_action( 'loop_end', [ $this, 'set_loop_end' ], 10, 0 );
add_action( 'transition_post_status', [ $this, 'transition_ad_status' ], 10, 3 );
// register debug parameter
$this->debug_parameter();
Advanced_Ads_Display_Conditions::get_instance();
( new Advanced_Ads_Frontend_Checks() );
Advanced_Ads_Ad_Health_Notices::get_instance();
}
/**
* Allow add-ons to hook
*/
public function advanced_ads_loaded() {
do_action( 'advanced-ads-plugin-loaded' );
}
/**
* Load filters to inject ads into various sections of our site
*/
public function init_injection() {
add_filter( 'the_content', [ $this, 'inject_content' ], $this->get_content_injection_priority() );
}
/**
* Log error messages when debug is enabled
*
* @param string $message error message.
* @link http://www.smashingmagazine.com/2011/03/08/ten-things-every-wordpress-plugin-developer-should-know/
*/
public static function log( $message ) {
if ( true === WP_DEBUG ) {
if ( is_array( $message ) || is_object( $message ) ) {
error_log( __( 'Advanced Ads Error following:', 'advanced-ads' ) );
error_log( print_r( $message, true ) );
} else {
/* translators: %s is an error message generated by the plugin. */
$message = sprintf( __( 'Advanced Ads Error: %s', 'advanced-ads' ), $message );
error_log( $message );
}
}
}
/**
* Compat method
*
* @return array with plugin options
*/
public function options() {
// we cant store options if WPML String Translations is enabled, or it would not translate the "Ad Label" option.
if ( ! isset( $this->options ) || class_exists( 'WPML_ST_String' ) ) {
$this->options = get_option( ADVADS_SLUG, [] );
}
// allow to change options dynamically
$this->options = apply_filters( 'advanced-ads-options', $this->options );
return $this->options;
}
/**
* Compat method
*
* @return array with adblocker options
*/
public function get_adblocker_options() {
// we cant store options if WPML String Translations is enabled, or it would not translate the "Ad Label" option.
if ( ! isset( $this->adblocker_options ) || class_exists( 'WPML_ST_String' ) ) {
$this->adblocker_options = wp_parse_args(
get_option( ADVADS_SETTINGS_ADBLOCKER, [] ),
[
'method' => false,
]
);
}
return $this->adblocker_options;
}
/**
* Compat method
*
* @return array with internal plugin options
*/
public function internal_options() {
if ( ! isset( $this->internal_options ) ) {
$defaults = [
'version' => ADVADS_VERSION,
'installed' => time(), // when was this installed.
];
$this->internal_options = get_option( ADVADS_SLUG . '-internal', [] );
// save defaults.
if ( [] === $this->internal_options ) {
$this->internal_options = $defaults;
$this->update_internal_options( $this->internal_options );
( new Capabilities() )->create_capabilities();
}
// for versions installed prior to 1.5.3 set installed date for now.
if ( ! isset( $this->internal_options['installed'] ) ) {
$this->internal_options['installed'] = time();
$this->update_internal_options( $this->internal_options );
}
}
return $this->internal_options;
}
/**
* Injected ad into content (before and after)
* Displays ALL ads
*
* @param string $content post content.
*
* @return string
*/
public function inject_content( $content = '' ) {
$options = $this->options();
// do not inject in content when on a BuddyPress profile upload page (avatar & cover image).
if ( ( function_exists( 'bp_is_user_change_avatar' ) && \bp_is_user_change_avatar() ) || ( function_exists( 'bp_is_user_change_cover_image' ) && bp_is_user_change_cover_image() ) ) {
return $content;
}
// do not inject ads multiple times, e.g., when the_content is applied multiple times.
if ( $this->has_many_the_content() ) {
return $content;
}
// Check if ads are disabled in secondary queries.
if ( ! empty( $options['disabled-ads']['secondary'] ) ) {
// this function was called by ajax (in secondary query).
if ( wp_doing_ajax() ) {
return $content;
}
// get out of wp_router_page post type if ads are disabled in secondary queries.
if ( 'wp_router_page' === get_post_type() ) {
return $content;
}
}
// No need to inject ads because all tags are stripped from excepts.
if ( doing_filter( 'get_the_excerpt' ) ) {
return $content;
}
// make sure that no ad is injected into another ad.
if ( get_post_type() === Constants::POST_TYPE_AD ) {
return $content;
}
// Do not inject on admin pages.
if ( is_admin() && ! wp_doing_ajax() ) {
return $content;
}
// Do not inject in writing REST requests.
if ( Conditional::is_gutenberg_writing_request() && Conditional::is_rest_request() ) {
return $content;
}
if ( ! $this->can_inject_into_content() ) {
return $content;
}
$placements = wp_advads_get_all_placements();
if ( ! apply_filters( 'advanced-ads-can-inject-into-content', true, $content, $placements ) ) {
return $content;
}
foreach ( $placements as $placement_id => $placement ) {
$item = $placement->get_item();
$type_object_id = $placement->get_type_object()->get_id();
if (
empty( $item )
|| 'default' === $type_object_id
|| ! apply_filters( 'advanced-ads-can-inject-into-content-' . $placement_id, true, $content, $placement )
) {
continue;
}
$placement_options = $placement->get_data();
switch ( $type_object_id ) {
case 'post_top':
$placement_content = get_the_placement(
$placement_id,
'',
$placement_options
);
$content = $placement_content . $content;
break;
case 'post_bottom':
$content .= get_the_placement(
$placement_id,
'',
$placement_options
);
break;
case 'post_content':
$content = Advanced_Ads_In_Content_Injector::inject_in_content( $placement_id, $placement_options, $content );
break;
}
}
if ( ! empty( Params::cookie( 'advads_frontend_picker' ) ) ) {
// Make possible to know where the content starts and ends.
$content = '<ins style="display: none;" class="advads-frontend-picker-boundary-helper"></ins>
' . $content;
}
return $content;
}
/**
* Whether injection using `the_content` is allowed
*
* @return bool
*/
private function can_inject_into_content() {
global $wp_query;
if ( is_feed() || Conditional::is_rest_request() ) {
return true;
}
$options = $this->options();
/**
* Allows experienced user to control up to the nth post ads will be injected via post content placement in a post list.
*
* Post displaying an excerpt instead of the full content will still be skipped (mainly controlled by the active theme).
*
* @param int|string $archive_injection_count the string "true" when ads is injected into all posts.
*/
$archive_injection_count = apply_filters( 'advanced-ads-content-injection-index', $options['content-injection-everywhere'] ?? 1 );
// Run only within the loop on single pages of public post types.
$public_post_types = get_post_types(
[
'public' => true,
'publicly_queryable' => true,
],
'names',
'or'
);
// Check if admin allows injection in all places.
$injection_enabled = $options['content-injection-enabled'] ?? 'off';
if ( ( $injection_enabled === 'off' || 0 === $archive_injection_count ) && ( ! is_singular( $public_post_types ) || ( ! Conditional::is_amp() && ! $this->in_the_loop() && ! $this->was_in_the_loop ) ) ) {
return false;
}
if ( is_main_query() && ! empty( $options ) && 'true' !== $archive_injection_count && isset( $wp_query->current_post ) && $wp_query->current_post >= $archive_injection_count ) {
return false;
}
return true;
}
/**
* General check if ads can be displayed for the whole page impression
*
* @return bool true, if ads can be displayed.
* @todo move this to set_disabled_constant().
*/
public function can_display_ads() {
$options = $this->options();
/**
* Check if ads are disabled on the currently displayed page. Allow user to define their own rules if needed.
*
* @param bool $ads_disabled whether ads are disabled by the plugin options.
* @param array $options plugin options.
*
* @return bool `true` if ads are disabled
*/
$ads_disabled = apply_filters( 'advanced-ads-disabled-ads', Conditional::is_ad_disabled(), $options );
if ( $ads_disabled ) {
return false;
}
// check if ads are disabled in secondary queries.
// and this is not main query and this is not ajax (because main query does not exist in ajax but ad needs to be shown).
if ( ! empty( $options['disabled-ads']['secondary'] ) && ! $this->is_main_query() && ! wp_doing_ajax() ) {
return false;
}
return true;
}
/**
* Add meta robots noindex, nofollow to images, which are part of 'Image ad' ad type
*/
public function noindex_attachment_images() {
global $post;
if ( is_attachment() && is_object( $post ) && isset( $post->post_parent ) ) {
$post_parent = get_post( $post->post_parent );
$parent_is_ad = $post_parent && Constants::POST_TYPE_AD === $post_parent->post_type;
// if the image was not attached to any post and if at least one image ad contains the image. Needed for backward compatibility.
$parent_is_image_ad = ( empty( $post->post_parent ) && 0 < get_post_meta( get_the_ID(), '_advanced-ads_parent_id', true ) );
if ( $parent_is_ad || $parent_is_image_ad ) {
echo '<meta name="robots" content="noindex,nofollow" />';
}
}
}
/**
* Show custom CSS in the header
*/
public function custom_header_code(){
if ( ! defined( 'ADVANCED_ADS_DISABLE_EDIT_BAR' ) && Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
?><style>
div.advads-edit-bar{position:absolute;height:0;display:none;z-index:10000;animation:advads-edit-appear 2s linear 1;}
@keyframes advads-edit-appear {
0% {opacity: 0.0;pointer-events: none;}
66% {opacity: 0.0;}
100% {opacity: 1.0;}
}
a.advads-edit-button{position:absolute;top:0;left:0;text-decoration:none !important;box-shadow:none;border-bottom:none;color:#0074a2;margin-top:-5px;}
a.advads-edit-button span{top:10px;line-height:25px;margin-left:-5px;width:26px;height:26px;border-radius:13px;border:solid 1px #0074a2;background:#fff}
<?php
printf(
'div[class^="%s"]:hover > div.advads-edit-bar {display: inline-block; vertical-align: top;}',
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
wp_advads()->get_frontend_prefix()
);
?>
</style>
<?php
}
}
/**
* Supports the "$this->is_main_query=true" while main query is being executed
*
* @param WP_Post $post The Post object (passed by reference).
* @param WP_Query $query The current Query object (passed by reference).
*/
public function set_query_type( $post, $query = null ) {
if ( $query instanceof WP_Query ) {
$this->is_main_query = $query->is_main_query();
}
}
/**
* Check if main query is being executed
*
* @return bool true while main query is being executed or not in the loop, false otherwise
*/
public function is_main_query() {
if ( ! $this->in_the_loop() ) {
// the secondary query check only designed for within post content.
return true;
}
return true === $this->is_main_query;
}
/**
* Sets whether the loop has started.
*/
public function set_loop_start() {
$this->in_the_loop = true;
}
/**
* Sets whether the loop has ended.
*/
public function set_loop_end() {
$this->in_the_loop = false;
}
/**
* Whether the loop has started and the caller is in the loop.
*
* @return bool
*/
public function in_the_loop() {
if ( in_the_loop() ) {
return true;
}
if ( $this->in_the_loop ) {
return true;
}
}
/**
* Find the calls to `the_content` inside functions hooked to `the_content`.
*
* @return bool
*/
public function has_many_the_content() {
global $wp_current_filter;
if ( count( array_keys( $wp_current_filter, 'the_content', true ) ) > 1 ) {
// More then one `the_content` in the stack.
return true;
}
return false;
}
/**
* Get an "Advertisement" label to use before single ad or before first ad in a group
*
* @param Ad|Group $item Ad or group.
* @param string $placement_state default/enabled/disabled.
*
* @return string label, empty string if label should not be displayed.
*/
public function get_label( $item, $placement_state = 'default' ) {
if ( 'disabled' === $placement_state ) {
return '';
}
$advads_options = self::get_instance()->options();
if ( 'enabled' !== $placement_state && empty( $advads_options['custom-label']['enabled'] ) ) {
return '';
}
$label = $advads_options['custom-label']['text'] ?? _x( 'Advertisements', 'label above ads', 'advanced-ads' );
$allowed_tags = [
'a' => [
'href' => true,
'title' => true,
],
'b' => [],
'blockquote' => [
'cite' => true,
],
'cite' => [],
'code' => [],
'del' => [
'datetime' => true,
],
'em' => [],
'i' => [],
'q' => [
'cite' => true,
],
's' => [],
'span' => [
'style' => true,
],
'strike' => [],
'br' => [],
'strong' => [],
];
// ad level label
if ( ! empty( $item->get_prop( 'ad_label' ) ) ) {
$label = $item->get_prop( 'ad_label' );
}
$label = ! empty( $advads_options['custom-label']['html_enabled'] ) ? wp_kses( $label, $allowed_tags ) : esc_html( $label );
$template = sprintf( '<div class="%s">%s</div>', wp_advads()->get_frontend_prefix() . 'adlabel', $label );
return apply_filters( 'advanced-ads-custom-label', $template, $label );
}
/**
* Retrieve the number of ads in any status
* excludes trash status by default
*
* @deprecated 1.48.2
*
* @param string|array $post_status default post status.
*
* @return int number of ads.
*/
public static function get_number_of_ads( $post_status = 'any' ) {
_deprecated_function( __METHOD__, '1.48.0', 'AdvancedAds\Utilities\WordPress::get_count_ads()' );
return WordPress::get_count_ads( $post_status );
}
/**
* Get the array with ad placements
*
* @deprecated 2.0.0 wp_advads_get_all_placements
*
* @return array $ad_placements
*/
public static function get_ad_placements_array() {
return wp_advads_get_all_placements();
}
/**
* Return the Advanced_Ads_Model responsible for loading ads, groups and placements into the frontend
*
* @deprecated 2.0.0 use new entity functions.
*
* @return mixed
*/
public function get_model() {
if ( ! isset( $this->model ) ) {
$this->model = new Advanced_Ads_Model();
}
return $this->model;
}
/**
* Store whether the loop started in an inner `the_content`.
*
* If so, let us assume that we are in the loop when we are in the outermost `the_content`.
* Makes sense only when a hooked to `the_content` function that produces an inner `the_content` has
* lesser priority then `$this->plugin->get_content_injection_priority()`.
*
* @param string $content Post content (unchanged).
*
* @return string
*/
public function set_was_in_the_loop( $content ) {
if ( self::get_instance()->has_many_the_content() ) {
$this->was_in_the_loop = $this->was_in_the_loop || $this->in_the_loop();
} else {
// Next top level `the_content`, forget that the loop started.
$this->was_in_the_loop = false;
}
return $content;
}
/**
* Listen to URL parameters for debugging
*/
private function debug_parameter() {
$referer = Params::server( 'HTTP_REFERER' );
if ( wp_doing_ajax() && $referer ) {
$query_string = wp_parse_url( $referer, PHP_URL_QUERY );
if ( $query_string ) {
parse_str( $query_string, $query );
}
if ( empty( $query['aa-debug'] ) ) {
return;
}
$debug_query = $query['aa-debug'];
} else {
$debug_query = Params::get( 'aa-debug' );
if ( empty( $debug_query ) ) {
return;
}
}
$parameters = explode( ',', sanitize_text_field( $debug_query ) );
foreach ( $parameters as $parameter ) {
switch ( trim( $parameter ) ) {
case 'dummy':
// switch all ads to "dummy"
add_filter( 'advanced-ads-ad-option-type', function() {
return 'dummy';
} );
break;
case 'vcoff':
// disable ad visitor conditions
add_filter( 'advanced-ads-ad-option-visitors', '__return_empty_array' );
break;
case 'cboff':
// disable cache-busting for all ads
add_filter( 'advanced-ads-ad-select-args', function( $args ) {
$args['cache-busting'] = 'ignore';
return $args;
} );
break;
}
}
}
/**
* Get priority used for injection inside content
*/
public function get_content_injection_priority() {
$options = $this->options();
return isset( $options['content-injection-priority'] ) ? (int) $options['content-injection-priority'] : 100;
}
/**
* Update internal plugin options
*
* @param array $options new internal options.
*/
public function update_internal_options( array $options ) {
// do not allow to clear options.
if ( [] === $options ) {
return;
}
$this->internal_options = $options;
update_option( ADVADS_SLUG . '-internal', $options );
}
/**
* Fires when a post is transitioned from one status to another.
*
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param WP_Post $post Post object.
*/
public function transition_ad_status( $new_status, $old_status, $post ) {
if ( ! isset( $post->post_type ) || Constants::POST_TYPE_AD !== $post->post_type || ! isset( $post->ID ) ) {
return;
}
$ad = wp_advads_get_ad( $post->ID );
if ( $old_status !== $new_status ) {
/**
* Fires when an ad has transitioned from one status to another.
*
* @param Ad $ad Ad object.
*/
do_action( "advanced-ads-ad-status-{$old_status}-to-{$new_status}", $ad );
}
if ( 'publish' === $new_status && 'publish' !== $old_status ) {
/**
* Fires when an ad has transitioned from any other status to `publish`.
*
* @param Ad $ad Ad object.
*/
do_action( 'advanced-ads-ad-status-published', $ad );
}
if ( 'publish' === $old_status && 'publish' !== $new_status ) {
/**
* Fires when an ad has transitioned from `publish` to any other status.
*
* @param Ad $ad Ad object.
*/
do_action( 'advanced-ads-ad-status-unpublished', $ad );
}
if ( $old_status === 'publish' && $new_status === Constants::AD_STATUS_EXPIRED ) {
/**
* Fires when an ad is expired.
*
* @param int $id
* @param Ad $ad
*/
do_action( 'advanced-ads-ad-expired', $ad->get_id(), $ad );
}
}
}