- 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>
173 lines
4.7 KiB
PHP
Executable File
173 lines
4.7 KiB
PHP
Executable File
<?php //phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase, WordPress.Files.FileName.InvalidClassFileName
|
|
|
|
use Symfony\Component\CssSelector\CssSelectorConverter;
|
|
|
|
/**
|
|
* Injects ads using an output buffer.
|
|
*
|
|
* Since we call `ob_start` during ad injection, we do not specify the callback in order to prevent the
|
|
* "ob_start(): Cannot use output buffering in output buffering display handlers" error.
|
|
*/
|
|
class Advanced_Ads_Pro_Module_Inject_Content_Custom_Position {
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function __construct() {
|
|
if ( 'php' !== Advanced_Ads_Pro::get_instance()->get_options()['placement-positioning'] ) {
|
|
return;
|
|
}
|
|
|
|
add_action( 'wp_head', [ $this, 'start_output_buffering' ], 20 );
|
|
// We need to inject ads (collect Cache Busting output) earlier than Cache Busting outputs its scripts at priority `21`.
|
|
add_action( 'wp_footer', [ $this, 'stop_output_buffering' ], 20 );
|
|
}
|
|
|
|
/**
|
|
* Start output buffering.
|
|
*/
|
|
public function start_output_buffering() {
|
|
ob_start();
|
|
}
|
|
|
|
/**
|
|
* Stop output buffering.
|
|
*/
|
|
public function stop_output_buffering() {
|
|
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- page content should be unescaped.
|
|
echo $this->maybe_inject_placements( ob_get_clean() );
|
|
}
|
|
|
|
/**
|
|
* Maybe inject some custom position placements.
|
|
*
|
|
* @param string $content Buffered page content.
|
|
* @return string
|
|
*/
|
|
public function maybe_inject_placements( $content ) {
|
|
if ( ! class_exists( 'Advanced_Ads_In_Content_Injector' ) ) {
|
|
return $content;
|
|
}
|
|
|
|
foreach ( wp_advads_get_placements() as $placement_id => $placement ) {
|
|
if (
|
|
empty( $placement->get_item() ) || ! $placement->is_type( [ 'custom_position', 'post_above_headline' ] )
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
$placement_options = $placement->get_data();
|
|
|
|
if ( $placement->is_type( 'custom_position' ) ) {
|
|
$xpath_options = $this->get_custom_position_xpath_options( $placement_options );
|
|
if ( ! $xpath_options ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( $placement->is_type( 'post_above_headline' ) ) {
|
|
$xpath_options = [
|
|
'tag' => 'custom',
|
|
'xpath' => '//h1',
|
|
'position' => 'before',
|
|
];
|
|
}
|
|
|
|
$content = Advanced_Ads_In_Content_Injector::inject_in_content(
|
|
$placement_id,
|
|
array_merge( $placement_options, $xpath_options ),
|
|
$content,
|
|
[
|
|
'allowEmpty' => true,
|
|
'alter_nodes' => false,
|
|
'itemLimit' => -1,
|
|
'repeat' => true,
|
|
'paragraph_id' => 1,
|
|
]
|
|
);
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Get XPath options for Custom Position placement.
|
|
*
|
|
* @param array $placement_options Placement options.
|
|
* @return array|bool XPath options or False on failure.
|
|
*/
|
|
private function get_custom_position_xpath_options( $placement_options ) {
|
|
if ( ( ! isset( $placement_options['inject_by'] ) || 'pro_custom_element' === $placement_options['inject_by'] ) && isset( $placement_options['pro_custom_element'] ) ) {
|
|
// By CSS selector.
|
|
$xpath = $this->css_to_xpath( $placement_options['pro_custom_element'] );
|
|
if ( ! $xpath ) {
|
|
return false;
|
|
}
|
|
|
|
$positions = [
|
|
'insertBefore' => 'before',
|
|
'prependTo' => 'prepend',
|
|
'appendTo' => 'append',
|
|
'insertAfter' => 'after',
|
|
];
|
|
|
|
return [
|
|
'tag' => 'custom',
|
|
'xpath' => $xpath,
|
|
'position' => isset( $placement_options['pro_custom_position'], $positions[ $placement_options['pro_custom_position'] ] )
|
|
? $positions[ $placement_options['pro_custom_position'] ]
|
|
: 'before',
|
|
];
|
|
} elseif ( isset( $placement_options['container_id'] ) ) {
|
|
// By HTML container.
|
|
$xpath = $this->css_to_xpath( $placement_options['container_id'] );
|
|
if ( ! $xpath ) {
|
|
return false;
|
|
}
|
|
return [
|
|
'tag' => 'custom',
|
|
'xpath' => $xpath,
|
|
'position' => 'append',
|
|
];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Translate a CSS expression into corresponding XPath expression.
|
|
*
|
|
* @param string $css CSS Expression.
|
|
* @return string XPath Expression.
|
|
*/
|
|
private function css_to_xpath( $css ) {
|
|
if ( ! $css ) {
|
|
return '';
|
|
}
|
|
|
|
// Our "frontend picker" adds the `:eq` selector.
|
|
// Since the "css-selector" library does not support it, we replace it with an unique tag.
|
|
$css = preg_replace( '/:eq\((\d+)\)/', ' > advads_eq_selector$1', $css );
|
|
|
|
try {
|
|
$query = ( new CssSelectorConverter() )->toXPath( $css );
|
|
} catch ( Exception $e ) {
|
|
return '';
|
|
}
|
|
|
|
// Remove the unique tag and implement the functionality of the `:eq` selector.
|
|
do {
|
|
$query = preg_replace_callback(
|
|
'/(.*?)\/advads_eq_selector(\d+)/',
|
|
static function ( $matches ) {
|
|
return sprintf( '(%s)[%s]', $matches[1], $matches[2] + 1 );
|
|
},
|
|
$query,
|
|
1,
|
|
$count
|
|
);
|
|
} while ( 1 === $count );
|
|
|
|
return $query;
|
|
}
|
|
}
|