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>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,556 @@
<?php // phpcs:ignoreFile
/**
* Ad blocker admin functionality.
*/
class Advanced_Ads_Ad_Blocker_Admin {
/**
* Singleton instance of the plugin
*
* @var Advanced_Ads_Ad_Blocker_Admin
*/
protected static $instance;
/**
* Module options
*
* @var array (if loaded)
*/
protected $options;
/**
* Pattern to search assets using preg_match. The string ends with .css/.js/.png/.gif
*
* @var string
*/
protected $search_file_pattern = '/(css|js|png|gif)$/';
/**
* Pattern to exclide directories from search. The string does not contain '/vendor/' or '/lib/' or '/admin/' or /node_modules/
*
* @var string
*/
protected $exclude_dir_pattern = '/(\/vendor\/|\/lib\/|\/admin\/|\/node_modules\/)/';
/**
* Array, containing path information on the currently configured uploads directory
*
* @var array
*/
protected $upload_dir;
/**
* Error messages for user
*
* @var WP_Error
*/
protected $error_messages;
/**
* Initialize the module
*
*/
private function __construct() {
// add module settings to Advanced Ads settings page
add_action( 'advanced-ads-settings-init', [ $this, 'settings_init' ], 9 );
$is_main_site = is_main_site( get_current_blog_id() );
if ( ! $is_main_site ) {
return;
}
// Get the most recent options values
$this->options = Advanced_Ads_Ad_Blocker::get_instance()->options();
$this->upload_dir = $this->options['upload_dir'];
add_action( 'admin_init', [ $this, 'process_auto_update' ] );
$this->error_messages = new WP_Error();
}
/**
* Return an instance of Advanced_Ads_Ad_Blocker
*
* @return Advanced_Ads_Ad_Blocker_Admin
*/
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;
}
/**
* Add settings to settings page.
*/
public function settings_init() {
add_settings_field(
'use-adblocker',
__( 'Ad blocker disguise', 'advanced-ads' ),
[ $this, 'render_settings_use_adblocker' ],
ADVADS_SETTINGS_ADBLOCKER,
'advanced_ads_adblocker_setting_section'
);
}
/**
* Render setting to enable/disable 'adblocker disguise'.
*/
public function render_settings_use_adblocker() {
$is_main_site = is_main_site( get_current_blog_id() );
$checked = ! empty( Advanced_Ads::get_instance()->get_adblocker_options()['use-adblocker'] );
include ADVADS_AB_BASE_PATH . 'admin/views/setting-use-adblocker.php';
// if this is a sub site in a network, don't run the rebuild form code.
if ( ! $is_main_site ) {
return;
}
// add the rebuild form directly after the settings
?>
<div id="advads-adblocker-wrapper" <?php echo( $checked ? '' : 'style="display: none;"' ); ?>>
<?php
$button_disabled = true;
$upload_dir = $this->upload_dir;
$options = $this->options;
include ADVADS_AB_BASE_PATH . 'admin/views/rebuild_form.php';
?>
</div>
<?php
}
/**
* Render the ad-blocker rebuild assets form
*
*/
public function add_asset_rebuild_form() {
global $wp_filesystem;
$success = false;
$message = '';
$fs_connect = Advanced_Ads_Filesystem::get_instance()->fs_connect( $this->upload_dir['basedir'] );
if ( $fs_connect === false || is_wp_error( $fs_connect ) ) {
$message = __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'advanced-ads' );
if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
$message = esc_html( $wp_filesystem->errors->get_error_message() );
}
if ( is_wp_error( $fs_connect ) && $fs_connect->get_error_code() ) {
$message = esc_html( $fs_connect->get_error_message() );
}
} else {
$output = $this->process_form();
if ( is_wp_error( $output ) ) {
$message = $output->get_error_message();
} else {
$success = true;
$message = __( 'The asset folder was rebuilt successfully', 'advanced-ads' );
}
}
$upload_dir = $this->upload_dir;
$button_disabled = false;
$options = Advanced_Ads_Ad_Blocker::get_instance()->options( true );
include ADVADS_AB_BASE_PATH . 'admin/views/rebuild_form.php';
}
/**
* Perform processing of the rebuild_form, sent by user
*
* @return true|WP_Error true on success, WP_Error in case of error
**/
private function process_form() {
// at this point we do not need ftp/ssh credentials anymore
$form_post_fields = array_intersect_key( $_POST, [ 'advads_ab_assign_new_folder' => false ] );
$this->create_dummy_plugin( $form_post_fields );
if ( $error_messages = $this->error_messages->get_error_messages() ) {
foreach ( $error_messages as $error_message ) {
Advanced_Ads::log( __METHOD__ . ': ' . $error_message );
}
return $this->error_messages;
}
return true;
}
/**
* Creates dummy plugin and return new options, that need to be stored in database.
*
* @param array $form_post_fields options, POST data sent by user.
* @return array $new_options - options, that need to be stored in database.
*/
public function create_dummy_plugin( $form_post_fields = [] ) {
global $wp_filesystem;
$need_assign_new_name = isset( $form_post_fields['advads_ab_assign_new_folder'] );
if ( ! $this->upload_dir ) {
$message = __( 'There is no writable upload folder', 'advanced-ads' );
$this->error_messages->add( 'create_dummy_1', $message);
return false;
}
$new_options = [
'lookup_table' => isset( $this->options['lookup_table'] ) ? $this->options['lookup_table'] : [],
];
$new_options_error = $new_options;
// $new_options_error does not have the 'module_can_work' key - ad-blocker script will be inactive and the asset folder will be rebuilt next time
$new_options['module_can_work'] = true;
$existing_files = @scandir( $this->upload_dir['basedir'] );
if ( $existing_files ) {
$existing_files = array_diff( $existing_files, [ '..', '.' ] );
} else {
$existing_files = [];
}
if ( ! empty( $this->options['folder_name'] ) ) {
$new_options['folder_name'] = $new_options_error['folder_name'] = $this->options['folder_name'];
$old_folder_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( $this->upload_dir['basedir'] ) ) . $this->options['folder_name'];
if ( $wp_filesystem->exists( $old_folder_normalized ) ) {
if ( $need_assign_new_name ) {
$existing_files[] = (string) $new_options['folder_name'];
$new_folder_name = $this->generate_unique_name( $existing_files );
$new_folder_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( $this->upload_dir['basedir'] ) ) . $new_folder_name;
if ( ! $wp_filesystem->move( $old_folder_normalized, $new_folder_normalized ) ) {
/* translators: %s old folder name */
$message = sprintf( __( 'Unable to rename "%s" directory', 'advanced-ads' ), $old_folder_normalized );
$this->error_messages->add( 'create_dummy_2', $message);
return false;
}
$new_options['folder_name'] = $new_options_error['folder_name'] = $new_folder_name;
}
$is_rebuild_needed = count( $this->get_assets() );
// we have an error while the method is being executed
Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options_error );
if ( $is_rebuild_needed ) {
$lookup_table = $this->copy_assets( $new_options['folder_name'], $need_assign_new_name );
if ( ! $lookup_table ) {
/* translators: %s folder name */
$message = sprintf( __( 'Unable to copy assets to the "%s" directory', 'advanced-ads' ), $new_options['folder_name'] );
$this->error_messages->add( 'create_dummy_3', $message);
return false;
}
$new_options['lookup_table'] = $lookup_table;
}
} else {
// we have an error while the method is being executed
Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options_error );
// old folder does not exist, let's create it
$lookup_table = $this->copy_assets( $new_options['folder_name'] );
if ( ! $lookup_table ) {
/* translators: %s folder name */
$message = sprintf( __( 'Unable to copy assets to the "%s" directory', 'advanced-ads' ), $new_options['folder_name'] );
$this->error_messages->add( 'create_dummy_4', $message);
return false;
}
$new_options['lookup_table'] = $lookup_table;
}
} else {
// It seems this is the first time this plugin was ran, let's create everything we need in order to
// have this plugin function normally.
$new_folder_name = $this->generate_unique_name( $existing_files );
// Create a unique folder name
$new_options['folder_name'] = $new_options_error['folder_name'] = $new_folder_name;
// we have an error while the method is being executed
Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options_error );
// Copy the assets
$lookup_table = $this->copy_assets( $new_options['folder_name'] );
if ( ! $lookup_table ) {
$message = sprintf( __( 'Unable to copy assets to the "%s" directory', 'advanced-ads' ), $new_options['folder_name'] );
$this->error_messages->add( 'create_dummy_5', $message);
return false;
}
$new_options['lookup_table'] = $lookup_table;
}
// successful result, save options and rewrite previous error options
Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options );
Advanced_Ads_Ad_Health_Notices::get_instance()->remove( 'assets_expired' );
}
/**
* Copy all assets (JS/CSS) to the magic directory.
*
* @param string $folder_name Destination folder.
* @param bool $need_assign_new_name True if we need to assign new random names to assets.
* @return bool/array Bool false on failure, array lookup table on success.
*/
public function copy_assets( $folder_name, $need_assign_new_name = false ) {
global $wp_filesystem;
// Are we completely rebuilding the assets folder?
$asset_path = trailingslashit( $this->upload_dir['basedir'] ) . $folder_name ;
$asset_path_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( $this->upload_dir['basedir'] ) ) . $folder_name;
// already saved associations (original name => replaced name)
$rand_asset_names = [];
if ( $need_assign_new_name ) {
// Check if there is a previous asset folder
if ( $wp_filesystem->exists( $asset_path_normalized ) ) {
// Remove the old directory and its contents
if ( ! $wp_filesystem->rmdir( trailingslashit( $asset_path_normalized ), true ) ) {
/* translators: %s directory path */
$message = sprintf( __( 'We do not have direct write access to the "%s" directory', 'advanced-ads' ), $asset_path_normalized );
$this->error_messages->add( 'copy_assets_1', $message);
return false;
}
}
} elseif ( isset( $this->options['lookup_table'] ) ) {
foreach ( $this->options['lookup_table'] as $orig_path => $replaced_info ) {
$replaced_path = is_array( $replaced_info ) ? $replaced_info['path'] : $replaced_info;
$orig_path_components = preg_split('/\//', $orig_path, -1, PREG_SPLIT_NO_EMPTY);
$replaced_path_components = preg_split('/\//', $replaced_path, -1, PREG_SPLIT_NO_EMPTY);
// (css, style.css) => (1, 2.css)
foreach ( $orig_path_components as $k=> $orig_path_part ) {
$rand_asset_names[ $orig_path_part] = (string) $replaced_path_components[$k];
}
}
}
// Lookup_table contains associations between the original path of the asset and it path within our magic folder.
// I.e: [advanced-ads-layer/admin/assets/css/admin.css] => array( path => /12/34/56/78/1347107783.css, mtime => 99 ).
$assets = $this->get_assets();
if ( $need_assign_new_name ) {
$lookup_table = [];
} else {
$lookup_table = isset( $this->options['lookup_table'] ) ? $this->options['lookup_table'] : [];
}
/* Do not rename assets and folders. If, for example, some library uses in file.css something like this:
'background: url(/img/image.png)', you should add 'img') to this array */
$not_rename_assets = [ 'public', 'assets', 'js', 'css', 'fancybox', 'advanced.js', 'jquery.fancybox-1.3.4.css' ];
// Loop through all the found assets
foreach ( $assets as $file => $filemtime ) {
if ( ! file_exists( $file ) ) {
continue;
}
$first_cleanup = str_replace( WP_PLUGIN_DIR , '', $file );
$first_cleanup_dir = dirname( $first_cleanup );
$first_cleanup_filename = basename( $first_cleanup );
$first_cleanup_file_extension = pathinfo( $first_cleanup, PATHINFO_EXTENSION );
$path_components = preg_split('/\//', $first_cleanup_dir, -1, PREG_SPLIT_NO_EMPTY);
$path_components_new = [];
// Interate over directories.
foreach ( $path_components as $k => $dir ) {
if ( in_array( $dir, $not_rename_assets ) ) {
$path_components_new[ $k ] = $dir;
} elseif ( array_key_exists( $dir, $rand_asset_names ) ) {
$path_components_new[ $k ] = $rand_asset_names[ $dir ];
} else {
$new_rand_folder_name = $this->generate_unique_name( array_values( $rand_asset_names ) );
$path_components_new[ $k ] = $new_rand_folder_name;
$rand_asset_names[ $dir ] = (string) $new_rand_folder_name;
}
}
$new_dir_full = trailingslashit( $asset_path ) . trailingslashit( implode( '/', $path_components_new ) );
$new_dir_full_normalized = trailingslashit( $asset_path_normalized ) . trailingslashit( implode( '/', $path_components_new ) );
$new_dir = trailingslashit( implode( '/', $path_components_new ) );
if ( ! in_array( $first_cleanup_filename, $not_rename_assets ) && ( $first_cleanup_file_extension == 'js' || $first_cleanup_file_extension == 'css' ) ) {
if ( array_key_exists( $first_cleanup_filename, $rand_asset_names ) ) {
$new_abs_file = $new_dir_full_normalized . $rand_asset_names[$first_cleanup_filename];
$new_rel_file = $new_dir . $rand_asset_names[$first_cleanup_filename];
} else {
$new_filename = $this->generate_unique_name( array_values( $rand_asset_names ), $first_cleanup_file_extension );
$rand_asset_names[$first_cleanup_filename] = (string) $new_filename;
$new_abs_file = $new_dir_full_normalized . $new_filename;
$new_rel_file = $new_dir . $new_filename;
}
} else {
$new_abs_file = $new_dir_full_normalized . $first_cleanup_filename;
$new_rel_file = $new_dir . $first_cleanup_filename;
}
if ( ! file_exists( $new_dir_full_normalized ) ) {
// Create the path if it doesn't exist (prevents the copy() function from failing)
if ( ! Advanced_Ads_Filesystem::get_instance()->mkdir_p( $new_dir_full_normalized ) ) {
$message = sprintf( __( 'We do not have direct write access to the "%s" directory', 'advanced-ads' ), $this->upload_dir['basedir'] );
$this->error_messages->add( 'copy_assets_4', $message);
return false;
}
}
$file_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( dirname( $file ) ) ) . basename( $file );
// Copy the file to our new magic directory,
if ( ! $wp_filesystem->copy( $file_normalized, $new_abs_file, true, FS_CHMOD_FILE ) ) {
/* translators: %s directory path */
$message = sprintf( __( 'Unable to copy files to %s', 'advanced-ads' ), $asset_path_normalized );
$this->error_messages->add( 'copy_assets_5', $message);
return false;
}
$lookup_table[ $first_cleanup ] = [
'path' => $new_rel_file,
'mtime' => $filemtime,
];
}
return $lookup_table;
}
/**
* This function recursively searches for assets
*
* @param string $dir The directory to search in.
* @return Array with pairs: abs_filename => mtime.
*/
public function recursive_search_assets( $dir ) {
$assets = [];
$tree = glob( rtrim( $dir, '/' ) . '/*' );
if ( is_array( $tree ) ) {
foreach ( $tree as $file ) {
if ( is_dir( $file ) && ! preg_match( $this->exclude_dir_pattern, $file ) ) {
$assets = array_merge( $assets, $this->recursive_search_assets( $file ) );
} elseif ( is_file( $file ) && preg_match( $this->search_file_pattern, $file ) ) {
$assets[ $file ] = @filemtime( $file );
}
}
}
return $assets;
}
/**
* Returns new or modified assets and their mtimes.
*
* @return array
*/
public function get_assets() {
$new_files_info = $this->recursive_search_assets( trailingslashit( WP_PLUGIN_DIR ) . 'advanced-ads*' );
if ( ! isset( $this->options['lookup_table'] ) || ! isset( $this->upload_dir['basedir'] ) || ! isset( $this->options['folder_name'] ) ) {
return $new_files_info;
}
$asset_path = trailingslashit( trailingslashit( $this->upload_dir['basedir'] ) . $this->options['folder_name'] ) ;
$new_files = [];
foreach ( $new_files_info as $abs_file => $mtime ) {
$rel_file = str_replace( WP_PLUGIN_DIR , '', $abs_file );
if ( ! isset( $this->options['lookup_table'][ $rel_file ]['mtime'] ) ||
$this->options['lookup_table'][ $rel_file ]['mtime'] !== $mtime ||
! file_exists( $asset_path . $this->options['lookup_table'][$rel_file]['path'] )
) {
$new_files[ $abs_file ] = $mtime;
}
}
return $new_files;
}
/**
* Automatically updates assets
*
*/
public function process_auto_update() {
$advads_options = Advanced_Ads::get_instance()->get_adblocker_options();
if ( ! isset( $advads_options['use-adblocker'] )
|| ! $this->upload_dir
) { return; }
//if module is working without errors and there are new assets
if ( ! empty( $this->options['module_can_work'] ) && count( $this->get_assets() ) ) {
$fs_connect = Advanced_Ads_Filesystem::get_instance()->fs_connect( $this->upload_dir['basedir'] );
if ( false === $fs_connect || is_wp_error( $fs_connect ) ) {
// we can not update assets automatically. The user should visit the setting page and update assets manually
// disable module and show notice
unset( $this->options['module_can_work'] );
Advanced_Ads_Ad_Blocker::get_instance()->update_options( $this->options );
return;
}
$this->create_dummy_plugin();
// write errors to the log
if ( $error_messages = $this->error_messages->get_error_messages() ) {
foreach ( $error_messages as $error_message ) {
Advanced_Ads::log( __METHOD__ . ': ' . $error_message );
}
}
}
}
/**
* Generate unique name
*
* @param array $haystack array to check, that the returned string does not exist in this array
* @param string $extension Extension to append to the name.
* @return string unique name
*/
function generate_unique_name( $haystack = false, $extension = '' ) {
$extension = $extension ? '.' . $extension : '';
if ( $haystack ) {
$i = 0;
do {
$rand = (string) mt_rand( 1, 999 );
if ( ++$i < 100 ) {
$needle = (string) $rand . $extension;
} else {
$needle = (string) $rand . '_' . $i . $extension;
}
} while( in_array( $needle, $haystack ) );
return $needle;
}
$needle = (string) mt_rand( 1, 999 ) . $extension;
return $needle;
}
/**
* Clear assets (on uninstall)
*/
function clear_assets() {
$advads_options = Advanced_Ads::get_instance()->options();
if ( ! empty( $this->options['folder_name'] )
&& ! empty( $this->options['module_can_work'] )
&& $this->upload_dir
&& class_exists( 'WP_Filesystem_Direct', false )
) {
$wp_filesystem = new WP_Filesystem_Direct( new StdClass() );
$path = trailingslashit( $this->upload_dir['basedir'] ) . trailingslashit( $this->options['folder_name'] );
$wp_filesystem->rmdir( $path, true );
}
}
}

View File

@@ -0,0 +1,75 @@
<?php // phpcs:disable WordPress.Files.FileName.NotHyphenatedLowercase
/**
* Ad blocker disguise rebuild template
*
* @package AdvancedAds\Pro
* @var array $upload_dir wp_upload_dir response
* @var string $message Response message
* @var bool|null $success Whether request was successful
* @var bool $button_disabled If button should have disabled attribute.
*/
?>
<?php if ( ! empty( $message ) && isset( $success ) ) : ?>
<div class="<?php echo $success ? 'advads-check' : 'advads-error'; ?> advads-notice-inline is-dismissible">
<p><?php echo esc_html( $message ); ?></p>
</div>
<?php endif; ?>
<?php if ( ! empty( $upload_dir['error'] ) ) : ?>
<p class="advads-notice-inline advads-error"><?php esc_html_e( 'Upload folder is not writable', 'advanced-ads' ); ?></p>
<?php
return;
endif;
?>
<div id="advanced-ads-rebuild-assets-form">
<?php if ( ! empty( $options['folder_name'] ) && ! empty( $options['module_can_work'] ) ) : ?>
<table class="form-table" role="presentation">
<tbody>
<tr>
<th scope="row"><?php esc_html_e( 'Asset path', 'advanced-ads' ); ?></th>
<td><?php echo esc_html( trailingslashit( $upload_dir['basedir'] ) . $options['folder_name'] ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Asset URL', 'advanced-ads' ); ?></th>
<td><?php echo esc_html( trailingslashit( $upload_dir['baseurl'] ) . $options['folder_name'] ); ?></td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Rename assets', 'advanced-ads' ); ?></th>
<td>
<label>
<input type="checkbox" name="advads_ab_assign_new_folder">
<?php echo esc_html__( 'Check if you want to change the names of the assets', 'advanced-ads' ) . '.'; ?>
<span class="advads-help">
<span class="advads-tooltip" style="position: fixed; left: 693px; top: 387px;">
<?php esc_html_e( 'This feature relocates potentially blocked scripts to a new, randomly named folder to help bypass ad blockers. The folder receives updates during plugin updates. Occasional rebuilding of the asset folder prevents browsers from caching outdated versions. If you\'re already using a plugin that renames scripts, like Autoptimize or WP Rocket, turn off this feature to avoid conflicts.', 'advanced-ads' ); ?>
</span>
</span>
</label>
</td>
</tr>
</tbody>
</table>
<?php else : ?>
<p>
<?php
$folder = ! empty( $options['folder_name'] )
? trailingslashit( $upload_dir['basedir'] ) . $options['folder_name']
: $upload_dir['basedir'];
printf(
/* translators: placeholder is path to folder in uploads dir */
esc_html__( 'Please, rebuild the asset folder. All assets will be located in %s', 'advanced-ads' ),
sprintf( '<strong>%s</strong>', esc_attr( $folder ) )
);
?>
</p>
<?php endif; ?>
<p class="submit">
<button type="button" class="button button-primary" id="advads-adblocker-rebuild" <?php echo( $button_disabled ? 'disabled' : '' ); ?>>
<?php esc_html_e( 'Rebuild asset folder', 'advanced-ads' ); ?>
</button>
</p>
</div>

View File

@@ -0,0 +1,37 @@
<?php
/**
* The view to render the option.
*
* @package AdvancedAds
* @var boolean $checked True, when the option is checked.
* @var boolean $is_main_site True, when the site is the main site of the current network.
*/
?>
<label>
<?php if ( $is_main_site ) : ?>
<input id="advanced-ads-use-adblocker" type="checkbox" value="1" name="<?php echo esc_attr( ADVADS_SETTINGS_ADBLOCKER ); ?>[use-adblocker]" <?php checked( $checked, 1, true ); ?>>
<?php else : ?>
<?php esc_html_e( 'The ad block disguise can only be set by the super admin on the main site in the network.', 'advanced-ads' ); ?>
<?php endif ?>
<?php esc_html_e( 'Prevents ad blockers from breaking your website when blocking asset files (.js, .css).', 'advanced-ads' ); ?>
<?php if ( ! defined( 'AAP_VERSION' ) ) : ?>
<p>
<?php
printf(
wp_kses(
/* translators: %s is a URL. */
__( 'Learn how to display alternative content to ad block users <a href="%s" target="_blank">in the manual</a>.', 'advanced-ads' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
'https://wpadvancedads.com/manual/ad-blockers/#utm_source=advanced-ads&utm_medium=link&utm_campaign=adblock-manual'
);
?>
</p>
<?php endif; ?>
</label>

View File

@@ -0,0 +1,176 @@
<?php // phpcs:ignore WordPress.Files.FileName
/**
* Ad blocker frontend functionality.
*/
class Advanced_Ads_Ad_Blocker {
/**
* Singleton instance of the plugin
*
* @var Advanced_Ads_Ad_Blocker
*/
protected static $instance;
/**
* Module options
*
* @var array (if loaded)
*/
protected $options;
/**
* Plugins directory URL
*
* @var string
*/
protected $plugins_url;
/**
* Initialize the module
*/
private function __construct() {
$options = $this->options();
if (
! empty( $options['use-adblocker'] ) &&
! empty( $options['folder_name'] ) &&
! empty( $options['module_can_work'] ) &&
$options['upload_dir']
) {
$this->plugins_url = plugins_url();
add_action( 'wp_enqueue_scripts', [ $this, 'edit_script_output' ], 101 );
}
}
/**
* Return an instance of Advanced_Ads_Ad_Blocker
*
* @return Advanced_Ads_Ad_Blocker
* @since 1.0.0
*/
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;
}
/**
* Edit the script output (URL's) for all advanced-ads plugins
*
* @since 1.0.0
*/
public function edit_script_output() {
global $wp_scripts, $wp_styles;
$options = $this->options();
// Check if the asset folder is set (check if this is installed yet).
if ( isset( $options['folder_name'] ) && '' !== $options['folder_name'] ) {
// Loop through all script files and change the URL from which they are loaded.
if ( is_object( $wp_scripts ) && is_array( $wp_scripts->registered ) ) {
foreach ( $wp_scripts->registered as $script ) {
if ( $script->src && is_string( $script->src ) && strpos( $script->src, 'advanced-ads' ) !== false ) {
$script->src = $this->clean_up_filename( $script->src );
}
}
}
// Loop through all style files and change the URL from which they are loaded.
if ( is_array( $wp_styles->registered ) ) {
foreach ( $wp_styles->registered as $style ) {
if ( false !== strpos( $style->src, 'advanced-ads' ) ) {
$style->src = $this->clean_up_filename( $style->src );
}
}
}
}
}
/**
* Clean up the filename
*
* @param string $file File to clean up.
*
* @return string
*/
public function clean_up_filename( $file ) {
$options = $this->options();
$upload_dir = $options['upload_dir'];
$url = str_replace( $this->plugins_url, '', $file );
if ( isset( $options['lookup_table'][ $url ] ) && is_array( $options['lookup_table'][ $url ] ) && isset( $options['lookup_table'][ $url ]['path'] ) ) {
return trailingslashit( $upload_dir['baseurl'] ) . trailingslashit( $options['folder_name'] ) . $options['lookup_table'][ $url ]['path'];
} elseif ( isset( $options['lookup_table'][ $url ] ) ) {
return trailingslashit( $upload_dir['baseurl'] ) . trailingslashit( $options['folder_name'] ) . $options['lookup_table'][ $url ];
}
return $file;
}
/**
* Return module options
*
* @param bool $force Whether the options should be fetched regardless if it has already been done. Is needed in AJAX calls.
*
* @return array
*/
public function options( $force = false ) {
if ( ! isset( $this->options ) || $force ) {
if ( function_exists( 'is_multisite' ) && is_multisite() ) {
global $current_site;
// Switch to main blog.
switch_to_blog( $current_site->blog_id );
$this->options = get_option( ADVADS_AB_SLUG, [] );
$advads_options = (array) get_option( ADVADS_SETTINGS_ADBLOCKER, [] );
$upload_dir = wp_upload_dir();
restore_current_blog();
} else {
$this->options = get_option( ADVADS_AB_SLUG, [] );
$advads_options = Advanced_Ads::get_instance()->get_adblocker_options();
$upload_dir = wp_upload_dir();
}
if ( ! $this->options ) {
$this->options = [];
}
$this->options['use-adblocker'] = ! empty( $advads_options['use-adblocker'] );
if ( $upload_dir['error'] ) {
$this->options['upload_dir'] = false;
} else {
$upload_dir['url'] = set_url_scheme( $upload_dir['url'] );
$upload_dir['baseurl'] = set_url_scheme( $upload_dir['baseurl'] );
// array, that has indices 'basedir' and 'baseurl'.
$this->options['upload_dir'] = $upload_dir;
}
}
return $this->options;
}
/**
* Update module options.
*
* @param array $new_options New options.
*/
public function update_options( $new_options ) {
if ( ! is_array( $new_options ) ) {
return;
}
update_option( ADVADS_AB_SLUG, $new_options );
// We do not save the following keys to the database.
if ( isset( $this->options['use-adblocker'] ) ) {
$new_options['use-adblocker'] = $this->options['use-adblocker'];
}
if ( isset( $this->options['upload_dir'] ) ) {
$new_options['upload_dir'] = $this->options['upload_dir'];
}
$this->options = $new_options;
}
}

View File

@@ -0,0 +1,13 @@
<?php
// module configuration
$path = dirname( __FILE__ );
return [
'classmap' => [
'Advanced_Ads_Ad_Blocker' => $path . '/classes/plugin.php',
'Advanced_Ads_Ad_Blocker_Admin' => $path . '/admin/admin.php',
],
'textdomain' => null,
];

View File

@@ -0,0 +1,24 @@
<?php //phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols -- the sniff does not accept calling add_action and declaring the callback in the same file
// Only load if not already existing (maybe included from another plugin).
if ( defined( 'ADVADS_AB_BASE_PATH' ) ) {
return;
}
// load basic path to the plugin
define( 'ADVADS_AB_BASE_PATH', plugin_dir_path( __FILE__ ) );
// general and global slug, e.g. to store options in WP, textdomain
define( 'ADVADS_AB_SLUG', 'advanced-ads-ab-module' );
add_action( 'advanced-ads-plugin-loaded', 'advanced_ads_load_adblocker' );
/**
* Load ad blocker functionality.
*/
function advanced_ads_load_adblocker() {
Advanced_Ads_Ad_Blocker::get_instance();
if ( is_admin() && ! wp_doing_ajax() ) {
Advanced_Ads_Ad_Blocker_Admin::get_instance();
}
}

View File

@@ -0,0 +1,150 @@
.advads-ad-positioning {
margin-bottom: -20px !important;
}
.advads-ad-positioning-position-groups-wrapper {
display: flex !important;
float: none !important;
}
.advads-ad-positioning-position-group {
display: flex;
flex-wrap: wrap;
flex-basis: 163px;
row-gap: 20px;
column-gap: 14px;
margin-left: 35px;
margin-right: 36px;
position: relative;
}
.advads-ad-positioning-position-group::after {
content: "";
display: block;
height: 100%;
width: 1px;
background-color: #c3c4c7;
position: absolute;
right: -35px;
top: 0;
}
.advads-ad-positioning-position-group:first-child {
margin-left: 0;
}
.advads-ad-positioning-position-group:last-child {
margin-right: 0;
}
.advads-ad-positioning-position-group:last-child::after {
display: none;
}
.advads-ad-positioning-position-group-heading, .advads-ad-positioning-position-group-description {
margin: 0;
flex-basis: 100%;
}
.advads-ad-positioning-position-group-heading {
font-size: 13px;
}
.advads-ad-positioning-position-wrapper {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
}
.advads-ad-positioning-position-wrapper.is-checked .advads-ad-positioning-position-icon path {
fill: #fff;
}
.advads-ad-positioning-position-wrapper.is-checked .advads-ad-positioning-position-icon .background {
fill: #0074A2;
}
.advads-ad-positioning-position-wrapper:hover .advads-ad-positioning-position-icon path, .advads-ad-positioning-position-wrapper:focus .advads-ad-positioning-position-icon path {
fill: #fff;
}
.advads-ad-positioning-position-wrapper:hover .advads-ad-positioning-position-icon .background, .advads-ad-positioning-position-wrapper:focus .advads-ad-positioning-position-icon .background {
fill: #0074A2;
}
.advads-ad-positioning-position-icon {
width: 45px;
height: 45px;
}
.advads-ad-positioning-position-icon svg {
width: 100%;
height: 100%;
}
#advads-ad-positioning-position-right_float ~ .advads-ad-positioning-position-icon, #advads-ad-positioning-position-right_nofloat ~ .advads-ad-positioning-position-icon {
transform: rotateZ(180deg);
}
.advads-ad-positioning-position-option {
position: absolute;
z-index: -1;
}
.advads-ad-positioning-spacing-wrapper {
display: inline-grid !important;
float: none !important;
grid-template-rows: auto 17px auto 17px auto;
grid-template-columns: auto 17px auto 17px auto;
grid-template-areas: ". . top . ." ". . icon_top . ." "left icon_left ad icon_right right" ". . icon_bottom . ." ". . bottom . legend";
}
label[for=advads-ad-positioning-spacing-top] {
grid-area: top;
}
label[for=advads-ad-positioning-spacing-right] {
grid-area: right;
}
label[for=advads-ad-positioning-spacing-bottom] {
grid-area: bottom;
}
label[for=advads-ad-positioning-spacing-left] {
grid-area: left;
}
.advads-ad-positioning-spacing-option {
width: 65px;
}
.advads-ad-positioning-spacing-option[type=number] {
padding-right: 2px;
}
.advads-ad-positioning-spacing-legend {
grid-area: legend;
align-self: end;
justify-self: end;
}
.advads-ad-positioning-spacing-direction {
justify-self: center;
align-self: center;
}
.advads-ad-positioning-spacing-direction svg path {
transform-origin: center;
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-top {
grid-area: icon_top;
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-top svg path {
transform: rotate(0deg);
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-right {
grid-area: icon_right;
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-right svg path {
transform: rotate(90deg);
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-bottom {
grid-area: icon_bottom;
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-bottom svg path {
transform: rotate(180deg);
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-left {
grid-area: icon_left;
}
.advads-ad-positioning-spacing-direction.advads-ad-positioning-spacing-left svg path {
transform: rotate(270deg);
}
.advads-ad-positioning-spacing-adcenter {
grid-area: ad;
background-color: var(--advads-ci-lightblue);
color: #fff;
border-radius: 4px;
text-align: center;
align-self: center;
line-height: 30px;
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30px" height="30px" viewBox="0 0 30 30" overflow="visible" enable-background="new 0 0 30 30" xml:space="preserve">
<path class="background" fill="#F2FCFE" d="M0,27.001C0,28.656,1.344,30,3,30h24c1.656,0,3-1.344,3-2.999V3c0-1.657-1.344-3-3-3H3C1.344,0,0,1.343,0,3
V27.001z"/>
<path fill="#66ACC7" d="M25.501,5.25H4.5c-0.414,0-0.75-0.335-0.75-0.75S4.086,3.75,4.5,3.75h21.001c0.414,0,0.75,0.335,0.75,0.75
S25.915,5.25,25.501,5.25z"/>
<path fill="#66ACC7" d="M25.501,8.25H4.5c-0.414,0-0.75-0.335-0.75-0.75S4.086,6.75,4.5,6.75h21.001c0.414,0,0.75,0.335,0.75,0.75
S25.915,8.25,25.501,8.25z"/>
<path fill="#66ACC7" d="M25.5,26.25H4.499c-0.414,0-0.75-0.336-0.75-0.75c0-0.415,0.336-0.75,0.75-0.75H25.5
c0.414,0,0.75,0.335,0.75,0.75C26.25,25.914,25.914,26.25,25.5,26.25z"/>
<path fill="#66ACC7" d="M25.5,23.249H4.499c-0.414,0-0.75-0.336-0.75-0.751c0-0.414,0.336-0.75,0.75-0.75H25.5
c0.414,0,0.75,0.336,0.75,0.75C26.25,22.913,25.914,23.249,25.5,23.249z"/>
<path fill="#0074A2" d="M19.5,9.75h-9c-0.415,0-0.75,0.335-0.75,0.75v9c0,0.415,0.335,0.751,0.75,0.751h9
c0.415,0,0.75-0.336,0.75-0.751v-9C20.25,10.086,19.915,9.75,19.5,9.75z M15,18c-1.654,0-3-1.345-3-3s1.346-3,3-3
s3.001,1.346,3.001,3S16.654,18,15,18z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30px" height="30px" viewBox="0 0 30 30" overflow="visible" enable-background="new 0 0 30 30" xml:space="preserve">
<path class="background" fill="#F2FCFE" d="M0,27.001C0,28.656,1.344,30,3,30h24c1.656,0,3-1.344,3-2.999V3c0-1.657-1.344-3-3-3H3C1.344,0,0,1.343,0,3
V27.001z"/>
<path fill="#66ACC7" d="M25.501,5.25H4.5c-0.414,0-0.75-0.335-0.75-0.75S4.086,3.75,4.5,3.75h21.001c0.414,0,0.75,0.335,0.75,0.75
S25.915,5.25,25.501,5.25z"/>
<path fill="#66ACC7" d="M25.501,8.25H4.5c-0.414,0-0.75-0.335-0.75-0.75S4.086,6.75,4.5,6.75h21.001c0.414,0,0.75,0.335,0.75,0.75
S25.915,8.25,25.501,8.25z"/>
<path fill="#66ACC7" d="M25.5,26.25H4.499c-0.414,0-0.75-0.336-0.75-0.75c0-0.415,0.336-0.75,0.75-0.75H25.5
c0.414,0,0.75,0.335,0.75,0.75C26.25,25.914,25.914,26.25,25.5,26.25z"/>
<path fill="#66ACC7" d="M25.5,23.249H4.499c-0.414,0-0.75-0.336-0.75-0.751c0-0.414,0.336-0.75,0.75-0.75H25.5
c0.414,0,0.75,0.336,0.75,0.75C26.25,22.913,25.914,23.249,25.5,23.249z"/>
<path fill="#0074A2" d="M13.499,9.75h-9c-0.414,0-0.75,0.335-0.75,0.75v9c0,0.415,0.336,0.751,0.75,0.751h9
c0.415,0,0.751-0.336,0.751-0.751v-9C14.25,10.086,13.914,9.75,13.499,9.75z M11.249,17.25c0,0.268-0.144,0.516-0.375,0.65
c-0.116,0.067-0.246,0.1-0.375,0.1s-0.26-0.033-0.375-0.1l-3.897-2.25C5.994,15.516,5.851,15.269,5.851,15s0.144-0.516,0.376-0.649
l3.897-2.25c0.231-0.135,0.519-0.135,0.75,0c0.231,0.134,0.375,0.381,0.375,0.649V17.25z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30px" height="30px" viewBox="0 0 30 30" overflow="visible" enable-background="new 0 0 30 30" xml:space="preserve">
<path class="background" fill="#F2FCFE" d="M0,27c0,1.656,1.344,3,3,3h24c1.656,0,3-1.344,3-3V3c0-1.657-1.344-3-3-3H3C1.344,0,0,1.343,0,3V27z"/>
<path fill="#0074A2" d="M20.25,10.5h-7.205v-3c0-0.268-0.145-0.516-0.376-0.649c-0.231-0.135-0.519-0.135-0.75,0l-7.794,4.5
C3.894,11.484,3.75,11.732,3.75,12s0.144,0.516,0.375,0.65l7.794,4.499c0.116,0.067,0.246,0.102,0.375,0.102
c0.13,0,0.26-0.034,0.375-0.102c0.231-0.133,0.376-0.381,0.376-0.648v-3h7.205c1.655,0,3.001,1.346,3.001,3
c0,1.654-1.346,3.001-3.001,3.001h-5.706c-0.829,0-1.499,0.67-1.499,1.5c0,0.828,0.67,1.5,1.499,1.5h5.706c3.309,0,6-2.691,6-6.001
C26.25,13.192,23.559,10.5,20.25,10.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30px" height="30px" viewBox="0 0 30 30" overflow="visible" enable-background="new 0 0 30 30" xml:space="preserve">
<path class="background" fill="#F2FCFE" d="M0,27.001C0,28.656,1.344,30,3,30h24c1.656,0,3-1.344,3-2.999v-24C30,1.343,28.656,0,27,0H3
C1.344,0,0,1.343,0,3V27.001z"/>
<path fill="#66ACC7" d="M25.501,5.25H4.5c-0.414,0-0.75-0.335-0.75-0.75S4.086,3.75,4.5,3.75h21.001c0.414,0,0.75,0.335,0.75,0.75
S25.915,5.25,25.501,5.25z"/>
<path fill="#66ACC7" d="M25.501,8.251H4.5c-0.414,0-0.75-0.335-0.75-0.75s0.336-0.75,0.75-0.75h21.001c0.414,0,0.75,0.335,0.75,0.75
S25.915,8.251,25.501,8.251z"/>
<path fill="#66ACC7" d="M25.501,11.25h-9c-0.415,0-0.751-0.335-0.751-0.75s0.336-0.75,0.751-0.75h9c0.414,0,0.75,0.335,0.75,0.75
S25.915,11.25,25.501,11.25z"/>
<path fill="#66ACC7" d="M25.501,14.25h-9c-0.415,0-0.751-0.335-0.751-0.75c0-0.415,0.336-0.75,0.751-0.75h9
c0.414,0,0.75,0.335,0.75,0.75C26.251,13.914,25.915,14.25,25.501,14.25z"/>
<path fill="#66ACC7" d="M25.501,17.25h-9c-0.415,0-0.751-0.335-0.751-0.75c0-0.414,0.336-0.75,0.751-0.75h9
c0.414,0,0.75,0.335,0.75,0.75C26.251,16.915,25.915,17.25,25.501,17.25z"/>
<path fill="#66ACC7" d="M25.501,20.252h-9c-0.415,0-0.751-0.336-0.751-0.751c0-0.414,0.336-0.75,0.751-0.75h9
c0.414,0,0.75,0.336,0.75,0.75C26.251,19.916,25.915,20.252,25.501,20.252z"/>
<path fill="#66ACC7" d="M25.5,26.25H4.499c-0.414,0-0.75-0.336-0.75-0.75c0-0.415,0.336-0.75,0.75-0.75H25.5
c0.414,0,0.75,0.335,0.75,0.75C26.25,25.914,25.914,26.25,25.5,26.25z"/>
<path fill="#66ACC7" d="M25.5,23.249H4.499c-0.414,0-0.75-0.336-0.75-0.751c0-0.414,0.336-0.75,0.75-0.75H25.5
c0.414,0,0.75,0.336,0.75,0.75C26.25,22.913,25.914,23.249,25.5,23.249z"/>
<path fill="#0074A2" d="M13.499,9.75h-9c-0.414,0-0.75,0.335-0.75,0.75v9c0,0.415,0.336,0.751,0.75,0.751h9
c0.415,0,0.751-0.336,0.751-0.751v-9C14.25,10.086,13.914,9.75,13.499,9.75z M11.249,17.25c0,0.268-0.144,0.516-0.375,0.65
c-0.116,0.067-0.246,0.1-0.375,0.1s-0.26-0.033-0.375-0.1l-3.897-2.25C5.994,15.516,5.851,15.269,5.851,15s0.144-0.516,0.376-0.649
l3.897-2.25c0.231-0.135,0.519-0.135,0.75,0c0.231,0.134,0.375,0.381,0.375,0.649V17.25z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="10px" height="9px" viewBox="0 0 10 9" overflow="visible" enable-background="new 0 0 10 9" xml:space="preserve">
<defs>
</defs>
<path fill="#0074A2" d="M0,7.75c0-0.216,0.056-0.433,0.171-0.626l3.748-6.497C4.14,0.239,4.553,0,4.999,0
c0.447,0,0.859,0.239,1.082,0.626l3.751,6.497c0.225,0.386,0.225,0.865,0,1.251C9.608,8.76,9.195,9,8.749,9H1.251
c-0.445,0-0.859-0.24-1.08-0.625C0.056,8.182,0,7.964,0,7.75z"/>
</svg>

After

Width:  |  Height:  |  Size: 682 B

View File

@@ -0,0 +1,20 @@
( () => {
const labels = document.getElementsByClassName( 'advads-ad-positioning-position-wrapper' ),
options = document.getElementsByClassName( 'advads-ad-positioning-position-option' ),
marginLeftInput = document.getElementById( 'advads-ad-positioning-spacing-left' ),
marginRightInput = document.getElementById( 'advads-ad-positioning-spacing-right' );
for ( const option of options ) {
option.addEventListener( 'change', event => {
for ( let label of labels ) {
label.classList.remove( 'is-checked' );
}
option.parentElement.classList.add( 'is-checked' );
const position = event.target.value.split( '_' );
marginLeftInput.readOnly = position[0] === 'center';
marginRightInput.readOnly = position[0] === 'center';
} );
}
} )();

View File

@@ -0,0 +1,215 @@
<?php // phpcs:ignoreFile
use AdvancedAds\Abstracts\Ad;
/**
* Class handling the ad positioning and migrating values from previous solutions.
*/
class Advanced_Ads_Ad_Positioning {
/**
* The instance of the current ad.
*
* @var Ad
*/
private $ad;
/**
* The structure of these output options.
*
* @var array
*/
private $positioning = [
'position' => 'none',
'clearfix' => false,
'margin' => [
'top' => 0,
'left' => 0,
'bottom' => 0,
'right' => 0,
],
];
/**
* Class constructor.
*
* @param Ad $ad The current ad object.
*/
public function __construct( Ad $ad ) {
$this->ad = $ad;
$this->migrate_values();
$this->filter_values();
}
/**
* Migrate option from a previous solution where floating was an additional setting.
*
* @return void
*/
private function migrate_values() {
$options = $this->get_options();
$this->positioning['margin'] = array_merge(
$this->positioning['margin'],
array_map( function($value) { return (int)$value; }, $options['margin'] )
);
$this->positioning['position'] = $options['position'];
// instead of having an empty value, set an explicit default.
if ( empty( $this->positioning['position'] ) ) {
$this->positioning['position'] = 'none';
$this->positioning['clearfix'] = false;
}
// left, center, right are the old values, if it's none of these we've already migrated.
if ( ! in_array( $this->positioning['position'], [ 'left', 'center', 'right' ], true ) ) {
// ensure we get an array with min two elements.
$position = explode( '_', $this->positioning['position'] . '_' );
// explicitly set clearfix option.
$this->positioning['clearfix'] = $position[0] !== 'center' && $position[1] === 'nofloat';
return;
}
if ( $this->positioning['position'] === 'center' ) {
$this->positioning['position'] = 'center_nofloat';
return;
}
$this->positioning['clearfix'] = ! empty( $options['clearfix'] );
$this->positioning['position'] .= $this->positioning['clearfix'] ? '_nofloat' : '_float';
}
/**
* Filter the option value for Ad.
* This ensures we don't have to update the whole positioning process but can change only the wp-admin side of things.
*
* @return void
*/
private function filter_values() {
foreach ( $this->positioning as $key => $value ) {
add_filter( "advanced-ads-ad-option-output.{$key}", function() use ( $value ) {
return $value;
} );
if ( is_array( $value ) ) {
foreach ( $value as $sub_key => $sub_value ) {
add_filter( "advanced-ads-ad-option-output.{$sub_key}", function() use ( $sub_value ) {
return $sub_value;
} );
}
}
}
}
/**
* Set up the positioning options with title, description and icon.
*
* @return array
*/
private function setup_positioning_options() {
return [
'default' => [
'title' => __( "Themes Default", 'advanced-ads' ),
'description' => __( 'The ad will behave as predefined by the theme.', 'advanced-ads' ),
'options' => [
'none' => [],
],
],
'float' => [
'title' => _x( 'Float', 'Layout options "Text Flow" heading', 'advanced-ads' ),
'description' => __( 'Text will wrap around the ad and its margin.', 'advanced-ads' ),
'options' => [
'left_float' => [],
'right_float' => [],
],
],
'block' => [
'title' => _x( 'Block', 'Layout options "Text Flow" heading', 'advanced-ads' ),
'description' => __( 'Text will continue after the ad and its margin.', 'advanced-ads' ),
'options' => [
'left_nofloat' => [
'img' => 'block-lr',
],
'center_nofloat' => [
'img' => 'block-cntr',
],
'right_nofloat' => [
'img' => 'block-lr',
],
],
],
];
}
/**
* Concatenate the templates and prepare inline styles and scripts.
*
* @return string
*/
public function return_admin_view() {
return $this->positioning_admin_view() . $this->spacing_admin_view();
}
/**
* Include the positioning view.
*
* @return string
*/
private function positioning_admin_view() {
$positioning = $this->positioning['position'];
$positioning_options = $this->setup_positioning_options();
ob_start();
include_once __DIR__ . '/../views/ad-positioning.php';
return ob_get_clean();
}
/**
* Include the spacing/margin view.
*
* @return string
*/
private function spacing_admin_view() {
$is_centered = explode( '_', $this->positioning['position'] )[0] === 'center';
$spacings = [
'top' => [
'label' => _x( 'Top', 'Ad positioning spacing label', 'advanced-ads' ),
],
'right' => [
'label' => _x( 'Right', 'Ad positioning spacing label', 'advanced-ads' ),
],
'bottom' => [
'label' => _x( 'Bottom', 'Ad positioning spacing label', 'advanced-ads' ),
],
'left' => [
'label' => _x( 'Left', 'Ad positioning spacing label', 'advanced-ads' ),
],
];
foreach ( $spacings as $direction => $item ) {
$spacings[ $direction ]['value'] = (int) $this->positioning['margin'][ $direction ];
}
ob_start();
include_once __DIR__ . '/../views/ad-spacing.php';
return ob_get_clean();
}
/**
* Get a well-formed array to work with.
*
* @return array
*/
private function get_options() {
$options = [
'position' => $this->ad->get_position(),
'clearfix' => $this->ad->get_clearfix(),
'margin' => $this->ad->get_margin(),
];
return wp_parse_args( $options, $this->positioning );
}
}

View File

@@ -0,0 +1,49 @@
<?php // phpcs:ignoreFile
/**
* Template for ad positioning.
*
* @var string $positioning how should the ad be positioned.
* @var array $spacing the spacing around the ad.
* @var array $positioning_options array of positioning options.
*/
?>
<div class="advads-ad-positioning-position advads-option-list">
<span class="label"><?php esc_html_e( 'Text Flow', 'advanced-ads' ); ?></span>
<div class="advads-ad-positioning-position-groups-wrapper">
<?php foreach ( $positioning_options as $group_name => $group ) : ?>
<div class="advads-ad-positioning-position-group">
<h3 class="advads-ad-positioning-position-group-heading"><?php echo esc_html( $group['title'] ); ?></h3>
<?php foreach ( $group['options'] as $option_name => $option ) : ?>
<?php $input_id = 'advads-ad-positioning-position-' . $option_name; ?>
<label
class="advads-ad-positioning-position-wrapper<?php echo( $option_name === $positioning ? ' is-checked' : '' ); ?>"
for="<?php echo esc_attr( $input_id ); ?>"
>
<input
type="radio"
class="advads-ad-positioning-position-option"
name="advanced_ad[output][position]"
id="<?php echo esc_attr( $input_id ); ?>"
value="<?php echo esc_attr( $option_name ); ?>"
<?php checked( $option_name, $positioning ); ?>
>
<div class="advads-ad-positioning-position-icon">
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- allow inline svg
echo preg_replace( '/\s+/', ' ', file_get_contents( sprintf( ADVADS_ABSPATH . 'modules/ad-positioning/assets/img/advads-bknd-ui-pos-%s.svg', esc_attr( isset( $option['img'] ) ? $option['img'] : $group_name ) ) ) );
?>
</div>
</label>
<?php endforeach; ?>
<p class="advads-ad-positioning-position-group-description">
<?php echo esc_html( $group['description'] ); ?>
</p>
</div>
<?php endforeach; ?>
</div>
</div>

View File

@@ -0,0 +1,49 @@
<?php
/**
* Settings for the spacing
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.x.x
*
* @var array $spacings array with values for top, right, bottom, left spacing pixel values.
* @var bool $is_centered Whether the "Block Center" position has been selected.
*/
?>
<hr class"advads-hide-in-wizard">
<div class="advads-ad-positioning-spacing advads-option-list">
<span class="label"><?php esc_html_e( 'Margin', 'advanced-ads' ); ?></span>
<div class="advads-ad-positioning-spacing-wrapper">
<?php foreach ( $spacings as $direction => $spacing ) : ?>
<?php $input_id = 'advads-ad-positioning-spacing-' . $direction; ?>
<label for="<?php echo esc_attr( $input_id ); ?>">
<span class="label screen-reader-text"><?php echo esc_html( $spacing['label'] ); ?></span>
<input
type="number"
step="1"
id="<?php echo esc_attr( $input_id ); ?>"
class="advads-ad-positioning-spacing-option"
name="advanced_ad[output][margin][<?php echo esc_attr( $direction ); ?>]"
value="<?php echo esc_attr( $spacing['value'] ); ?>"
<?php __checked_selected_helper( $is_centered && ( in_array( $direction, [ 'left', 'right' ], true ) ), true, true, 'readonly' ); ?>
>
</label>
<div class="advads-ad-positioning-spacing-direction <?php echo esc_attr( $input_id ); ?>" aria-hidden="true">
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- allow inline svg
echo preg_replace( '/\s+/', ' ', file_get_contents( ADVADS_ABSPATH . 'modules/ad-positioning/assets/img/advads-bknd-ui-pos-margin.svg' ) ); // phpcs:ignore
?>
</div>
<?php endforeach; ?>
<div class="advads-ad-positioning-spacing-adcenter" aria-hidden="true">
<?php esc_html_e( 'Ad', 'advanced-ads' ); ?>
</div>
<span class="advads-ad-positioning-spacing-legend"><?php echo esc_html_x( 'in px', 'Ad positioning spacing legend text', 'advanced-ads' ); ?></span>
</div>
</div>

View File

@@ -0,0 +1,70 @@
<?php // phpcs:ignore WordPress.Files.FileName
/**
* Logic to render options for ads, groups and placements
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
/**
* Class Advanced_Ads_Adblock_Finder_Admin
*/
class Advanced_Ads_Adblock_Finder_Admin {
/**
* Advanced_Ads_Adblock_Finder_Admin constructor.
*/
public function __construct() {
add_filter( 'advanced-ads-setting-tabs', [ $this, 'add_tabs' ], 50 );
add_action( 'advanced-ads-settings-init', [ $this, 'settings_init' ], 9 );
}
/**
* Add tabs to the settings page.
*
* @param array $tabs setting tabs.
*
* @return array
*/
public function add_tabs( array $tabs ): array {
$tabs['adblocker'] = [
'page' => ADVADS_SETTINGS_ADBLOCKER,
'group' => ADVADS_SETTINGS_ADBLOCKER,
'tabid' => 'adblocker',
'title' => __( 'Ad Blocker', 'advanced-ads' ),
];
return $tabs;
}
/**
* Add settings to settings page.
*/
public function settings_init() {
register_setting( ADVADS_SETTINGS_ADBLOCKER, ADVADS_SETTINGS_ADBLOCKER );
add_settings_section(
'advanced_ads_adblocker_setting_section',
__( 'Ad Blocker', 'advanced-ads' ),
'__return_empty_string',
ADVADS_SETTINGS_ADBLOCKER
);
add_settings_field(
'GA-tracking-id',
__( 'Ad blocker counter', 'advanced-ads' ),
[ $this, 'render_settings_ga' ],
ADVADS_SETTINGS_ADBLOCKER,
'advanced_ads_adblocker_setting_section'
);
}
/**
* Render input for the Google Analytics Tracking ID.
*/
public function render_settings_ga() {
$options = Advanced_Ads::get_instance()->get_adblocker_options();
$ga_uid = isset( $options['ga-UID'] ) ? $options['ga-UID'] : '';
include_once __DIR__ . '/views/setting-ga.php';
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Input for Google Analytics property ID.
*
* @package AdvancedAds
* @var string $ga_uid Google Analytics property ID
*/
?>
<label>
<input type="text" name="<?php echo esc_attr( ADVADS_SETTINGS_ADBLOCKER ); ?>[ga-UID]" value="<?php echo esc_attr( $ga_uid ); ?>"/>
<?php esc_html_e( 'Google Analytics Tracking ID', 'advanced-ads' ); ?>
</label>
<p class="description">
<?php
printf(
/* translators: %s is demo GA4 ID. */
esc_html__(
'Enter your Google Analytics property ID (e.g. %s) above to track the page views of visitors who use an ad blocker.',
'advanced-ads'
),
'<code>G-A12BC3D456</code>'
);
?>
</p>

View File

@@ -0,0 +1,13 @@
<?php
// Module configuration.
$path = dirname( __FILE__ );
return [
'classmap' => [
'Advanced_Ads_Adblock_Finder' => $path . '/public/public.php',
'Advanced_Ads_Adblock_Finder_Admin' => $path . '/admin/admin.php',
],
'textdomain' => null,
];

View File

@@ -0,0 +1,9 @@
<?php
class_exists( 'Advanced_Ads', false ) || exit();
if ( ! is_admin() ) {
new Advanced_Ads_Adblock_Finder;
} elseif ( ! wp_doing_ajax() ) {
new Advanced_Ads_Adblock_Finder_Admin;
}

View File

@@ -0,0 +1,62 @@
/**
* Check if an ad blocker is enabled.
*
* @param function callback A callback function that is executed after the check has been done.
* The 'isEnabled' (bool) variable is passed as the callback's first argument.
*/
window.advanced_ads_check_adblocker = (function (callback) {
let pendingCallbacks = [];
let isEnabled = null;
function RAF(RAF_callback) {
const fn =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (RAF_callback) {
return setTimeout(RAF_callback, 16);
};
fn.call(window, RAF_callback);
}
RAF(function () {
// Create a bait.
const ad = document.createElement('div');
ad.innerHTML = '&nbsp;';
ad.setAttribute('class', 'ad_unit ad-unit text-ad text_ad pub_300x250');
ad.setAttribute(
'style',
'width: 1px !important; height: 1px !important; position: absolute !important; left: 0px !important; top: 0px !important; overflow: hidden !important;'
);
document.body.appendChild(ad);
RAF(function () {
const styles = window.getComputedStyle?.(ad);
const mozBinding = styles?.getPropertyValue('-moz-binding');
isEnabled =
(styles && styles.getPropertyValue('display') === 'none') ||
(typeof mozBinding === 'string' &&
mozBinding.indexOf('about:') !== -1);
// Call pending callbacks.
for (var i = 0, length = pendingCallbacks.length; i < length; i++) {
pendingCallbacks[i](isEnabled);
}
pendingCallbacks = [];
});
});
return function (callback) {
if ('undefined' === typeof advanced_ads_adblocker_test) {
isEnabled = true;
}
if (isEnabled === null) {
pendingCallbacks.push(callback);
return;
}
// Run the callback immediately
callback(isEnabled);
};
})();

View File

@@ -0,0 +1 @@
window.advanced_ads_check_adblocker=function(){var t=[],n=null;function e(t){var n=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(t){return setTimeout(t,16)};n.call(window,t)}return e((function(){var i=document.createElement("div");i.innerHTML="&nbsp;",i.setAttribute("class","ad_unit ad-unit text-ad text_ad pub_300x250"),i.setAttribute("style","width: 1px !important; height: 1px !important; position: absolute !important; left: 0px !important; top: 0px !important; overflow: hidden !important;"),document.body.appendChild(i),e((function(){var e,o,a=null===(e=(o=window).getComputedStyle)||void 0===e?void 0:e.call(o,i),d=null==a?void 0:a.getPropertyValue("-moz-binding");n=a&&"none"===a.getPropertyValue("display")||"string"==typeof d&&-1!==d.indexOf("about:");for(var r=0,u=t.length;r<u;r++)t[r](n);t=[]}))})),function(e){"undefined"==typeof advanced_ads_adblocker_test&&(n=!0),null!==n?e(n):t.push(e)}}();

View File

@@ -0,0 +1,49 @@
function AdvAdsAdBlockCounterGA( UID ) {
this.UID = UID;
this.analyticsObject = typeof gtag === 'function';
var self = this;
this.count = function () {
gtag( 'event', 'AdBlock', {
'event_category': 'Advanced Ads',
'event_label': 'Yes',
'non_interaction': true,
'send_to': self.UID
} );
};
// pseudo-constructor
( function () {
if ( ! self.analyticsObject ) {
// No one has requested gtag.js at this point, require it.
var script = document.createElement( 'script' );
script.src = 'https://www.googletagmanager.com/gtag/js?id=' + UID;
script.async = true;
document.body.appendChild( script );
window.dataLayer = window.dataLayer || [];
window.gtag = function () {
dataLayer.push( arguments );
};
self.analyticsObject = true;
gtag( 'js', new Date() );
}
var config = {'send_page_view': false, 'transport_type': 'beacon'};
if ( window.advanced_ads_ga_anonymIP ) {
config.anonymize_ip = true;
}
gtag( 'config', UID, config );
} )();
return this;
}
advanced_ads_check_adblocker( function ( is_enabled ) {
// Send data to Google Analytics if an ad blocker was detected.
if ( is_enabled ) {
new AdvAdsAdBlockCounterGA( advanced_ads_ga_UID ).count();
}
} );

View File

@@ -0,0 +1 @@
window.advanced_ads_check_adblocker=function(){var t=[],n=null;function e(t){var n=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(t){return setTimeout(t,16)};n.call(window,t)}return e((function(){var a=document.createElement("div");a.innerHTML="&nbsp;",a.setAttribute("class","ad_unit ad-unit text-ad text_ad pub_300x250"),a.setAttribute("style","width: 1px !important; height: 1px !important; position: absolute !important; left: 0px !important; top: 0px !important; overflow: hidden !important;"),document.body.appendChild(a),e((function(){var e,o,i=null===(e=(o=window).getComputedStyle)||void 0===e?void 0:e.call(o,a),d=null==i?void 0:i.getPropertyValue("-moz-binding");n=i&&"none"===i.getPropertyValue("display")||"string"==typeof d&&-1!==d.indexOf("about:");for(var c=0,r=t.length;c<r;c++)t[c](n);t=[]}))})),function(e){"undefined"==typeof advanced_ads_adblocker_test&&(n=!0),null!==n?e(n):t.push(e)}}(),(()=>{function t(t){this.UID=t,this.analyticsObject="function"==typeof gtag;var n=this;return this.count=function(){gtag("event","AdBlock",{event_category:"Advanced Ads",event_label:"Yes",non_interaction:!0,send_to:n.UID})},function(){if(!n.analyticsObject){var e=document.createElement("script");e.src="https://www.googletagmanager.com/gtag/js?id="+t,e.async=!0,document.body.appendChild(e),window.dataLayer=window.dataLayer||[],window.gtag=function(){dataLayer.push(arguments)},n.analyticsObject=!0,gtag("js",new Date)}var a={send_page_view:!1,transport_type:"beacon"};window.advanced_ads_ga_anonymIP&&(a.anonymize_ip=!0),gtag("config",t,a)}(),this}advanced_ads_check_adblocker((function(n){n&&new t(advanced_ads_ga_UID).count()}))})();

View File

@@ -0,0 +1,71 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Ad block finder module frontend helper class
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
use AdvancedAds\Options;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Arr;
/**
* Class Advanced_Ads_Adblock_Finder
*/
class Advanced_Ads_Adblock_Finder {
/**
* Advanced_Ads_Adblock_Finder constructor.
*/
public function __construct() {
add_action( 'wp_footer', [ $this, 'print_adblock_check_js' ], 9 );
}
/**
* Print the appropriate script into wp_footer.
*
* Don't print anything on AMP pages.
* Print minimal script if Advanced Ads Pro module "Ads for ad blockers" is active.
*/
public function print_adblock_check_js() {
if ( Conditional::is_amp() ) {
return;
}
$options = Advanced_Ads::get_instance()->get_adblocker_options();
$minified = ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG;
$ad_blocker_options = Options::instance()->get( 'adblocker' );
// if ad blocker counter is active.
if ( ! empty( $options['ga-UID'] ) ) {
printf(
'<script>(function(){var advanced_ads_ga_UID="%s",advanced_ads_ga_anonymIP=!!%d;%s})();</script>',
esc_attr( $options['ga-UID'] ),
esc_attr(
! defined( 'ADVANCED_ADS_DISABLE_ANALYTICS_ANONYMIZE_IP' ) ||
! ADVANCED_ADS_DISABLE_ANALYTICS_ANONYMIZE_IP
),
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- escaping could break the script and we're getting the contents of a local file
$minified
? file_get_contents( __DIR__ . '/ga-adblock-counter.min.js' )
: file_get_contents( __DIR__ . '/adblocker-enabled.js' ) . file_get_contents( __DIR__ . '/ga-adblock-counter.js' )
// phpcs:enable
);
} elseif (
defined( 'AAP_SLUG' )
&& (
Options::instance()->get( 'adblocker.ads-for-adblockers.enabled' )
|| ( Arr::has( $ad_blocker_options, 'method' ) && 'nothing' !== $ad_blocker_options['method'] )
)
) {
// if Advanced Ads Pro module "Ads for ad blockers" is active but no tracking.
// or if method is not "nothing".
printf(
'<script>%s</script>',
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- escaping could break the script and we're getting the contents of a local file
file_get_contents( __DIR__ . '/adblocker-enabled' . ( $minified ? '.min' : '' ) . '.js' )
);
}
}
}

View File

@@ -0,0 +1,487 @@
<?php // phpcs:ignoreFile
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Conditional;
/**
* User interface for managing the 'ads.txt' file.
*/
class Advanced_Ads_Ads_Txt_Admin {
/**
* Ads.txt data management class
*
* @var Advanced_Ads_Ads_Txt_Strategy
*/
private $strategy;
/**
* Ads.txt frontend logic class
*
* @var Advanced_Ads_Ads_Txt_Public
*/
private $public;
/**
* AdSense network ID.
*/
const adsense = 'adsense';
const ACTION = 'wp_ajax_advads-ads-txt';
/**
* Whether the notices should be updated via AJAX because no cached data exists.
*
* @var bool
*/
private $notices_are_stale = false;
/**
* Constructor
*
* @param Advanced_Ads_Ads_Txt_Strategy $strategy Ads.txt data management class.
* @param Advanced_Ads_Ads_Txt_Public $public Ads.txt frontend logic class.
*/
public function __construct( Advanced_Ads_Ads_Txt_Strategy $strategy, Advanced_Ads_Ads_Txt_Public $public ) {
$this->strategy = $strategy;
$this->public = $public;
add_filter( 'advanced-ads-sanitize-settings', [ $this, 'toggle' ], 10, 1 );
add_action( 'pre_update_option_advanced-ads-adsense', [ $this, 'update_adsense_option' ], 10, 2 );
add_action( 'advanced-ads-settings-init', [ $this, 'add_settings' ] );
add_action( self::ACTION, [ $this, 'ajax_refresh_notices' ] );
}
/**
* Toggle ads.txt and add additional content.
*
* @param array $options Options.
* @return array $options Options.
*/
public function toggle( $options ) {
// phpcs:disable WordPress.Security.NonceVerification.Missing
$create = ! empty( Params::post( 'advads-ads-txt-create' ) );
$all_network = ! empty( Params::post( 'advads-ads-txt-all-network' ) );
$additional_content = trim( wp_unslash( Params::post( 'advads-ads-txt-additional-content', '' ) ) );
// phpcs:enable
$this->strategy->toggle( $create, $all_network, $additional_content );
$content = $this->get_adsense_blog_data();
$this->strategy->add_network_data( self::adsense, $content );
$r = $this->strategy->save_options();
if ( is_wp_error( $r ) ) {
add_settings_error(
'advanced-ads-adsense',
'adsense-ads-txt-created',
$r->get_error_message(),
'error'
);
}
return $options;
}
/**
* Update the 'ads.txt' file every time the AdSense settings are saved.
* The reason for not using `update_option_*` filter is that the function
* should also get called for newly added AdSense options.
*
* @param array $prev Previous options.
* @return array $new New options.
*/
public function update_adsense_option( $new, $prev ) {
if ( $new === $prev ) {
return $new;
}
$content = $this->get_adsense_blog_data( $new );
$this->strategy->add_network_data( self::adsense, $content );
$r = $this->strategy->save_options();
if ( is_wp_error( $r ) ) {
add_settings_error(
'advanced-ads-adsense',
'adsense-ads-txt-created',
$r->get_error_message(),
'error'
);
}
return $new;
}
/**
* Add setting fields.
*
* @param string $hook The slug-name of the settings page.
*/
public function add_settings( $hook ) {
$adsense_data = Advanced_Ads_AdSense_Data::get_instance();
$adsense_id = $adsense_data->get_adsense_id();
add_settings_section(
'advanced_ads_ads_txt_setting_section',
'ads.txt',
[ $this, 'render_ads_txt_section_callback' ],
$hook
);
add_settings_field(
'adsense-ads-txt-enable',
'',
[ $this, 'render_setting_toggle' ],
$hook,
'advanced_ads_ads_txt_setting_section'
);
add_settings_field(
'adsense-ads-txt-content',
'',
[ $this, 'render_setting_additional_content' ],
$hook,
'advanced_ads_ads_txt_setting_section'
);
}
public function render_ads_txt_section_callback() {}
/**
* Render toggle settings.
*/
public function render_setting_toggle() {
global $current_blog;
$domain = isset( $current_blog->domain ) ? $current_blog->domain : '';
$can_process_all_network = $this->can_process_all_network();
$is_all_network = $this->strategy->is_all_network();
$is_enabled = $this->strategy->is_enabled();
include dirname( __FILE__ ) . '/views/setting-create.php';
}
/**
* Render additional content settings.
*/
public function render_setting_additional_content() {
$content = $this->strategy->get_additional_content();
$notices = $this->get_notices();
$notices = $this->get_notices_markup( $notices );
$link = home_url( '/' ) . 'ads.txt';
$adsense_line = $this->get_adsense_blog_data();
include dirname( __FILE__ ) . '/views/setting-additional-content.php';
}
/**
* Check if other sites of the network can be processed by the user.
*
* @return bool
*/
private function can_process_all_network() {
return ! Advanced_Ads_Ads_Txt_Utils::is_subdir()
&& is_super_admin()
&& is_multisite();
}
/**
* Get notices.
*
* @return array Array of notices.
*/
public function get_notices() {
$url = home_url( '/' );
$parsed_url = wp_parse_url( $url );
$notices = [];
if ( ! isset( $parsed_url['scheme'] ) || ! isset ( $parsed_url['host'] ) ) {
return $notices;
}
$link = sprintf( '<a href="%1$s" target="_blank">%1$s</a>', esc_url( $url . 'ads.txt' ) );
$button = ' <button type="button" class="advads-ads-txt-action button" style="vertical-align: middle;" id="%s">%s</button>';
if ( ! $this->strategy->is_enabled() ) {
return $notices;
}
if ( Advanced_Ads_Ads_Txt_Utils::is_subdir() ) {
$notices[] = [
'advads-error-message',
sprintf(
/* translators: %s homepage link */
esc_html__( 'The ads.txt file cannot be placed because the URL contains a subdirectory. You need to make the file available at %s', 'advanced-ads' ),
sprintf( '<a href="%1$s" target="_blank">%1$s</a>', esc_url( $parsed_url['scheme'] . '://' . $parsed_url['host'] ) )
),
];
} else {
if ( null === ( $file = $this->get_notice( 'get_file_info', $url ) ) ) {
$this->notices_are_stale = true;
return $notices;
}
if ( ! is_wp_error( $file ) ) {
if ( $file['exists'] ) {
$notices[] = [
'',
sprintf(
/* translators: %s link of ads.txt */
esc_html__( 'The file is available on %s.', 'advanced-ads' ),
$link
),
];
} else {
$notices[] = [ '', esc_html__( 'The file was not created.', 'advanced-ads' ) ];
}
if ( $file['is_third_party'] ) {
/* translators: %s link */
$message = sprintf( esc_html__( 'A third-party file exists: %s', 'advanced-ads' ), $link );
if ( $this->can_edit_real_file() ) {
$message .= sprintf( $button, 'advads-ads-txt-remove-real', __( 'Import & Replace', 'advanced-ads' ) );
$message .= '<p class="description">'
. __( 'Move the content of the existing ads.txt file into Advanced Ads and remove it.', 'advanced-ads' )
. '</p>';
}
$notices['is_third_party'] = [ 'advads-error-message', $message ];
}
} else {
$notices[] = [
'advads-error-message',
sprintf(
/* translators: %s is replaced with an error message. */
esc_html__( 'An error occured: %s.', 'advanced-ads' ),
esc_html( $file->get_error_message() )
),
];
}
$need_file_on_root_domain = $this->get_notice( 'need_file_on_root_domain', $url );
if ( null === $need_file_on_root_domain ) {
$this->notices_are_stale = true;
return $notices;
}
if ( $need_file_on_root_domain ) {
$notices[] = [
'advads-ads-txt-nfor',
sprintf(
/* translators: %s the line that may need to be added manually */
esc_html__( 'If your site is located on a subdomain, you need to add the following line to the ads.txt file of the root domain: %s', 'advanced-ads' ),
// Without http://.
'<code>subdomain=' . esc_html( $parsed_url['host'] ) . '</code>'
),
];
}
}
return $notices;
}
/**
* Get HTML markup of the notices.
*
* @param array $notices Notices.
* @return string $r HTML markup.
*/
private function get_notices_markup( $notices ) {
if ( $this->notices_are_stale ) {
// Do not print `ul` to fetch notices via AJAX.
return '';
}
$r = '<ul id="advads-ads-txt-notices">';
foreach ( $notices as $notice ) {
$r .= sprintf( '<li class="%s">%s</li>', $notice[0], $notice[1] );
}
$r .= '</ul>';
return $r;
}
/**
* Check if the `ads.txt` file is displayed to visitors.
*
* @return bool True if displayed, False otherwise.
*/
public static function is_displayed() {
$url = home_url( '/' );
$file = self::get_notice( 'get_file_info', $url );
return is_array( $file ) && ! empty( $file['exists'] );
}
/**
* Get a notice.
*
* @return null/bool Boolean on success or null if no cached data exists.
* In the latter case, this function should be called using AJAX
* to get fresh data.
*/
public static function get_notice( $func, $url ) {
if ( ! method_exists( 'Advanced_Ads_Ads_Txt_Utils', $func ) ) {
return false;
}
$url = $url ? $url : home_url( '/' );
$key = self::get_transient_key();
$transient = get_transient( $key );
if ( ! wp_doing_ajax() || ! doing_action( self::ACTION ) ) {
return isset( $transient[ $func ] ) ? $transient[ $func ] : null;
}
$r = call_user_func( [ 'Advanced_Ads_Ads_Txt_Utils', $func ], $url );
$transient = is_array( $transient ) ? $transient : [];
$transient[ $func ] = $r;
set_transient( $key, $transient, WEEK_IN_SECONDS );
return $r;
}
/**
* Get Adsense data.
*
* @param array $new New data.
*
* @return string
*/
public function get_adsense_blog_data( $new = null ) {
if ( null === $new ) {
$new = Advanced_Ads_AdSense_Data::get_instance()->get_options();
}
$adsense_id = ! empty( $new['adsense-id'] ) ? trim( $new['adsense-id'] ) : '';
if ( ! $adsense_id ) {
return '';
}
$data = [
'domain' => 'google.com',
'account_id' => $adsense_id,
'account_type' => 'DIRECT',
'certification_authority' => 'f08c47fec0942fa0',
];
$result = implode( ', ', $data );
return $result;
}
/**
* Check if a third-party ads.txt file exists.
*/
public function ajax_refresh_notices() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
$response = [];
$action_notices = [];
$request_type = Params::request( 'type' );
if ( $request_type ) {
if ( 'remove_real_file' === $request_type ) {
$remove = $this->remove_real_file();
if ( is_wp_error( $remove ) ) {
$action_notices[] = [ 'advads-ads-txt-updated advads-notice-inline advads-error', $remove->get_error_message() ];
} else {
$action_notices[] = [
'advads-ads-txt-updated',
__( 'The ads.txt is now managed with Advanced Ads.', 'advanced-ads' )
];
$options = $this->strategy->get_options();
$response['additional_content'] = esc_textarea( $options['custom'] );
}
}
if ( 'create_real_file' === $request_type ) {
$action_notices[] = $this->create_real_file();
}
}
$notices = $this->get_notices();
$notices = array_merge( $notices, $action_notices );
$response['notices'] = $this->get_notices_markup( $notices );
wp_send_json( $response );
}
/**
* Connect to the filesystem.
*/
private function fs_connect() {
global $wp_filesystem;
$fs_connect = Advanced_Ads_Filesystem::get_instance()->fs_connect( [ ABSPATH ] );
if ( false === $fs_connect || is_wp_error( $fs_connect ) ) {
$message = __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'advanced-ads' );
if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
$message = esc_html( $wp_filesystem->errors->get_error_message() );
}
if ( is_wp_error( $fs_connect ) && $fs_connect->get_error_code() ) {
$message = esc_html( $fs_connect->get_error_message() );
}
return new WP_Error( 'can_not_connect', $message );
}
return true;
}
/**
* Remove existing real ads.txt file.
*/
public function remove_real_file() {
global $wp_filesystem;
if ( ! $this->can_edit_real_file() ) {
return new WP_Error( 'not_main_site', __( 'Not the main blog', 'advanced-ads' ) );
}
$fs_connect = $this->fs_connect();
if ( is_wp_error( $fs_connect ) ) {
return $fs_connect;
}
$abspath = trailingslashit( $wp_filesystem->abspath() );
$file = $abspath . 'ads.txt';
if ( $wp_filesystem->exists( $file ) && $wp_filesystem->is_file( $file ) ) {
$data = $wp_filesystem->get_contents( $file );
$tp_file = new Advanced_Ads_Ads_Txt_Real_File();
$tp_file->parse_file( $data );
$aa_data = $this->public->get_frontend_output();
$aa_file = new Advanced_Ads_Ads_Txt_Real_File();
$aa_file->parse_file( $aa_data );
$tp_file->subtract( $aa_file );
$output = $tp_file->output();
$this->strategy->set_additional_content( $output );
$this->strategy->save_options();
if ( $wp_filesystem->delete( $file ) ) {
return true;
} else {
return new WP_Error( 'could_not_delete', __( 'Could not delete the existing ads.txt file', 'advanced-ads' ) );
}
} else {
return new WP_Error( 'not_found', __( 'Could not find the existing ads.txt file', 'advanced-ads' ) );
}
}
/**
* Check if the user is alowed to edit real file.
*/
private function can_edit_real_file() {
return is_super_admin();
}
/**
* Get transient key.
*/
public static function get_transient_key() {
return 'advanced_ads_ads_txt_ctp' . home_url( '/') ;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* View to show the additional content setting for ads.txt.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
if ( $adsense_line ) : ?>
<p>
<?php
echo wp_kses(
sprintf(
/* translators: %s: The adsense line added automically by Advanced Ads. */
__( 'The following line will be added automatically because you connected your AdSense account with Advanced Ads: %s', 'advanced-ads' ),
'<br><code>' . $adsense_line . '</code>'
),
[
'br' => [],
'code' => [],
]
);
?>
</p>
<?php endif; ?>
<br />
<textarea cols="50" rows="5" id="advads-ads-txt-additional-content" name="advads-ads-txt-additional-content"><?php echo esc_textarea( $content ); ?></textarea>
<p class="description"><?php esc_html_e( 'Additional records to add to the file, one record per line. AdSense is added automatically.', 'advanced-ads' ); ?></p>
<div id="advads-ads-txt-notice-wrapper">
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $notices;
?>
</div>
<p class="advads-notice-inline advads-error hidden" id="advads-ads-txt-notice-error">
<?php
/* translators: %s is replaced with an error message. */
esc_html_e( 'An error occured: %s.', 'advanced-ads' );
?>
</p>
<button class="button advads-ads-txt-action" type="button" id="advads-ads-txt-notice-refresh"><?php esc_html_e( 'Check for problems', 'advanced-ads' ); ?></button>
<a href="<?php echo esc_url( $link ); ?>" class="button" target="_blank"><?php esc_html_e( 'Preview', 'advanced-ads' ); ?></button>

View File

@@ -0,0 +1,41 @@
<?php
/**
* View for the ads.txt creation setting.
*
* @package AdvancedAds
*
* @var bool $is_enabled
* @var bool $is_all_network
* @var bool $can_process_all_network
* @var string $domain
*/
?>
<div id="advads-ads-txt">
<label title="<?php esc_html_e( 'enabled', 'advanced-ads' ); ?>">
<input type="radio" name="advads-ads-txt-create" value="1" <?php checked( $is_enabled, true ); ?> />
<?php esc_html_e( 'enabled', 'advanced-ads' ); ?>
</label>
<label title="<?php esc_html_e( 'disabled', 'advanced-ads' ); ?>">
<input type="radio" name="advads-ads-txt-create" value="0" <?php checked( $is_enabled, false ); ?> />
<?php esc_html_e( 'disabled', 'advanced-ads' ); ?>
</label>
<span class="description">
<a target="_blank" href="https://wpadvancedads.com/manual/ads-txt/?utm_source=advanced-ads&utm_medium=link&utm_campaign=settings-ads-txt" class="advads-manual-link">
<?php esc_html_e( 'Manual', 'advanced-ads' ); ?>
</a>
</span>
<?php if ( $can_process_all_network ) : ?>
<p>
<label>
<input name="advads-ads-txt-all-network" type="checkbox"<?php checked( $is_all_network, true ); ?> />
<?php esc_html_e( 'Generate a single ads.txt file for all sites in the multisite network.', 'advanced-ads' ); ?>
</label>
</p>
<p class="description">
<?php esc_html_e( 'Usually, this should be enabled on the main site of the network - often the one without a subdomain or subdirectory.', 'advanced-ads' ); ?>
</p>
<?php endif; ?>
</div>

View File

@@ -0,0 +1,17 @@
<?php
/**
* Module configuration.
*/
$path = dirname( __FILE__ );
return [
'classmap' => [
'Advanced_Ads_Ads_Txt_Public' => $path . '/public/class-advanced-ads-ads-txt-public.php',
'Advanced_Ads_Ads_Txt_Strategy' => $path . '/includes/class-advanced-ads-ads-txt-strategy.php',
'Advanced_Ads_Ads_Txt_Admin' => $path . '/admin/class-advanced-ads-ads-txt-admin.php',
'Advanced_Ads_Ads_Txt_Utils' => $path . '/includes/class-advanced-ads-ads-txt-utils.php',
'Advanced_Ads_Ads_Txt_Real_File'=> $path . '/includes/class-advanced-ads-ads-txt-real-file.php',
],
'textdomain' => null,
];

View File

@@ -0,0 +1,97 @@
<?php
/**
* Represents a real ads.txt file.
*/
class Advanced_Ads_Ads_Txt_Real_File {
private $records = [];
/**
* Parse a real file.
*
* @param string $file File data.
*/
public function parse_file( $file ) {
$lines = preg_split( '/\r\n|\r|\n/', $file );
$comments = [];
foreach ( $lines as $line ) {
$line = explode( '#', $line );
if ( ! empty( $line[1] ) && $comment = trim( $line[1] ) ) {
$comments[] = '# ' . $comment;
}
if ( ! trim( $line[0] ) ) {
continue;
}
$rec = explode( ',', $line[0] );
$data = [];
foreach ( $rec as $k => $r ) {
$r = trim( $r, " \n\r\t," );
if ( $r ) {
$data[] = $r;
}
}
if ( $data ) {
// Add the record and comments that were placed above or to the right of it.
$this->add_record( implode( ', ', $data ), $comments );
}
$comments = [];
}
}
/**
* Add record.
*
* @string $data Record without comments.
* @array $comments Comments related to the record.
*/
private function add_record( $data, $comments = [] ) {
$this->records[] = [ $data, $comments ];
}
/**
* Get records
*
* @return array
*/
public function get_records() {
return $this->records;
}
/**
* Output file
*
* @return string
*/
public function output() {
$r = '';
foreach ( $this->records as $rec ) {
foreach ( $rec[1] as $rec1 ) {
$r .= $rec1 . "\n";
}
$r .= $rec[0] . "\n";
}
return $r;
}
/**
* Subtract another ads.txt file.
*
* @return string
*/
public function subtract( Advanced_Ads_Ads_Txt_Real_File $subtrahend ) {
$r1 = $subtrahend->get_records();
foreach ( $this->records as $k => $record ) {
foreach ( $r1 as $r ) {
if ( $record[0] === $r[0] ) {
unset( $this->records[ $k ] );
}
}
}
}
}

View File

@@ -0,0 +1,220 @@
<?php
/**
* Manage the data.
*/
class Advanced_Ads_Ads_Txt_Strategy {
const OPTION = 'advanced_ads_ads_txt';
private $options;
private $changed = false;
public function __construct() {
$this->options = $this->get_options();
$this->changed = false;
}
/**
* Whether to include records from other sites of the network.
*
* @return bool
*/
public function is_all_network() {
$options = $this->get_options();
return is_multisite() && $options['all_network'];
}
/**
* Is enabled.
*
* @return bool
*/
public function is_enabled() {
$this->options = $this->get_options();
return $this->options['enabled'];
}
/**
* Get additional content.
*
* @return string.
*/
public function get_additional_content() {
$options = $this->get_options();
return $options['custom'];
}
/**
* Toggle the file and add additional conent.
*
* @return bool.
*/
public function toggle( $is_enabled, $all_network, $additional_content ) {
$prev = $this->get_options();
$this->options['enabled'] = $is_enabled;
$this->options['all_network'] = $all_network;
$this->options['custom'] = $additional_content;
if ( $this->options !== $prev ) {
$this->changed = true;
}
return true;
}
/**
* Add ad network data.
*
* @param string $id Ad network id.
* @param string $rec A Record to add.
*
* @return bool
*/
public function add_network_data( $id, $rec ) {
$prev = $this->get_options();
$this->options['networks'][ $id ]['rec'] = $rec;
if ( $this->options !== $prev ) {
$this->changed = true;
}
return true;
}
/**
* Set additional content.
*
* @param string $custom Additional content.
* @param bool $replace Whether to replace or not.
*
* @return bool
*/
public function set_additional_content( $custom, $replace = false ) {
$prev = $this->get_options();
if ( $replace ) {
$this->options['custom'] = $custom;
} else {
$this->options['custom'] .= "\n" . $custom;
}
if ( $this->options !== $prev ) {
$this->changed = true;
}
return true;
}
/**
* Prepare content of a blog for output.
*
* @param array $options Options.
*
* @return string
*/
public function parse_content( $options ) {
$o = '';
foreach ( $options['networks'] as $_id => $data ) {
if ( ! empty( $data['rec'] ) ) {
$o .= $data['rec'] . "\n";
}
}
if ( ! empty( $options['custom'] ) ) {
$o .= $options['custom'] . "\n";
}
return $o;
}
/**
* Save options.
*
* @return bool
*/
public function save_options() {
if ( ! $this->changed ) {
return true;
}
$blog_id = get_current_blog_id();
$tmp_options = Advanced_Ads_Ads_Txt_Utils::remove_duplicate_lines(
[
$blog_id => $this->options,
]
);
$this->options = $tmp_options[ $blog_id ];
if ( is_multisite() ) {
update_site_meta(
get_current_blog_id(),
self::OPTION,
$this->options
);
} else {
update_option(
self::OPTION,
$this->options
);
}
$this->changed = false;
delete_transient( Advanced_Ads_Ads_Txt_Admin::get_transient_key() );
return true;
}
/**
* Get options.
*
* @return array
*/
public function get_options() {
if ( isset( $this->options ) ) {
return $this->options;
}
if ( is_multisite() ) {
$options = get_site_meta( get_current_blog_id(), self::OPTION, true );
} else {
$options = get_option( self::OPTION, [] );
}
if ( ! is_array( $options ) ) {
$options = [];
}
$this->options = $this->load_default_options( $options );
return $this->options;
}
/**
* Load default options.
*
* @param array $options Options.
*
* @return array
*/
public function load_default_options( $options ) {
if ( ! isset( $options['enabled'] ) ) {
$options['enabled'] = true;
}
if ( ! isset( $options['all_network'] ) ) {
$options['all_network'] = false;
}
if ( ! isset( $options['custom'] ) ) {
$options['custom'] = '';
}
if ( ! isset( $options['networks'] ) || ! is_array( $options['networks'] ) ) {
$options['networks'] = [];
}
return $options;
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
* User interface for managing the 'ads.txt' file.
*/
class Advanced_Ads_Ads_Txt_Utils {
private static $location;
/**
* Get file info.
*
* @param string $url Url to retrieve the file.
* @return array/WP_Error An array containing 'exists', 'is_third_party'.
* A WP_Error upon error.
*/
public static function get_file_info( $url = null ) {
$url = $url ? $url : home_url( '/' );
// Disable ssl verification to prevent errors on servers that are not properly configured with its https certificates.
/** This filter is documented in wp-includes/class-wp-http-streams.php */
$sslverify = apply_filters( 'https_local_ssl_verify', false );
$response = wp_remote_get(
trailingslashit( $url ) . 'ads.txt',
[
'timeout' => 3,
'sslverify' => $sslverify,
'headers' => [
'Cache-Control' => 'no-cache',
],
]
);
$code = wp_remote_retrieve_response_code( $response );
$content = wp_remote_retrieve_body( $response );
$content_type = wp_remote_retrieve_header( $response, 'content-type' );
if ( is_wp_error( $response ) ) {
return $response;
}
$file_exists = ! is_wp_error( $response )
&& 404 !== $code
&& ( false !== stripos( $content_type, 'text/plain' ) );
$header_exists = false !== strpos( $content, Advanced_Ads_Ads_Txt_Public::TOP );
$r = [
'exists' => $file_exists && $header_exists,
'is_third_party' => $file_exists && ! $header_exists
];
return $r;
}
/**
* Check if the another 'ads.txt' file should be hosted on the root domain.
*
* @return bool
*/
public static function need_file_on_root_domain( $url = null ) {
$url = $url ? $url : home_url( '/' );
$parsed_url = wp_parse_url( $url );
if ( ! isset( $parsed_url['host'] ) ) {
return false;
}
$host = $parsed_url['host'];
if ( WP_Http::is_ip_address( $host ) ) {
return false;
}
$host_parts = explode( '.', $host );
$count = count( $host_parts );
if ( $count < 3 ) {
return false;
}
if ( 3 === $count ) {
// Example: `http://one.{net/org/gov/edu/co}.two`.
$suffixes = [ 'net', 'org', 'gov', 'edu', 'co' ];
if ( in_array( $host_parts[ $count - 2 ], $suffixes, true ) ) {
return false;
}
// Example: `one.com.au'.
$suffix_and_tld = implode( '.', array_slice( $host_parts, 1 ) );
if ( in_array( $suffix_and_tld, [ 'com.au', 'com.br', 'com.pl' ] ) ) {
return false;
}
// `http://www.one.two` will only be crawled if `http://one.two` redirects to it.
// Check if such redirect exists.
if ( 'www' === $host_parts[0] ) {
/*
* Do not append `/ads.txt` because otherwise the redirect will not happen.
*/
$no_www_url = $parsed_url['scheme'] . '://' . trailingslashit( $host_parts[1] . '.' . $host_parts[2] );
add_action( 'requests-requests.before_redirect', [ __CLASS__, 'collect_locations' ] );
wp_remote_get( $no_www_url, [ 'timeout' => 5, 'redirection' => 3 ] );
remove_action( 'requests-requests.before_redirect', [ __CLASS__, 'collect_locations' ] );
$no_www_url_parsed = wp_parse_url( self::$location );
if ( isset( $no_www_url_parsed['host'] ) && $no_www_url_parsed['host'] === $host ) {
return false;
}
}
}
return true;
}
/**
* Collect last location.
*
* @return string $location An URL.
*/
public static function collect_locations( $location ) {
self::$location = $location;
}
/**
* Check if the site is in a subdirectory, for example 'http://one.two/three'.
*
* @return bool
*/
public static function is_subdir( $url = null ) {
$url = $url ? $url : home_url( '/' );
$parsed_url = wp_parse_url( $url );
if ( ! empty( $parsed_url['path'] ) && '/' !== $parsed_url['path'] ) {
return true;
}
return false;
}
/**
* Remove duplicate lines.
*
* @param array $blog_data Array of arrays of blog options, keyed by by blog IDs.
* @param array $options {
* Options.
*
* @type string $to_comments Whether to convert duplicate records to comments.
* }
* @return array $blog_data Array of arrays of blog options, keyed by by blog IDs.
*/
public static function remove_duplicate_lines( $blog_data, $options = [] ) {
$to_comments = ! empty( $options['to_comments'] );
$added_records = [];
foreach ( $blog_data as $blog_id => &$blog_options ) {
foreach ( $blog_options['networks'] as $id => $data ) {
// Convert to comments or remove duplicate records that are not comments.
if ( ! empty( $data['rec'] ) && '#' !== substr( $data['rec'], 0, 1 ) && in_array( $data['rec'], $added_records, true ) ) {
if ( $to_comments ) {
$blog_options['networks'][ $id ]['rec'] = '# ' . $blog_options['networks'][ $id ]['rec'];
} else {
unset( $blog_options['networks'][ $id ] );
}
continue;
}
$added_records[] = $data['rec'];
}
$blog_options['custom'] = explode( "\n", $blog_options['custom'] );
$blog_options['custom'] = array_map( 'trim', $blog_options['custom'] );
foreach ( $blog_options['custom'] as $id => $rec ) {
// Convert to comments or remove duplicate records that are not comments.
if ( ! empty( $rec ) && '#' !== substr( $rec, 0, 1 ) && in_array( $rec, $added_records, true ) ) {
if ( $to_comments ) {
$blog_options['custom'][ $id ] = '# ' . $blog_options['custom'][ $id ];
} else {
unset( $blog_options['custom'][ $id ] );
}
continue;
}
$added_records[] = $rec;
}
$blog_options['custom'] = implode( "\n", $blog_options['custom'] );
}
return $blog_data;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* Prepare content of the 'ads.txt' file.
*
* @package Advanced_Ads_Ads_Txt
*/
/**
* Initializes the ads.txt module for Advanced Ads plugin.
*
* @return void
*/
function advanced_ads_ads_txt_init(): void {
if (
! is_multisite()
|| ( function_exists( 'is_site_meta_supported' ) && is_site_meta_supported() )
) {
$public = new Advanced_Ads_Ads_Txt_Public( new Advanced_Ads_Ads_Txt_Strategy() );
if ( is_admin() ) {
new Advanced_Ads_Ads_Txt_Admin( new Advanced_Ads_Ads_Txt_Strategy(), $public );
} else {
new Advanced_Ads_Ads_Txt_Public( new Advanced_Ads_Ads_Txt_Strategy() );
}
}
}
add_action( 'advanced-ads-plugin-loaded', 'advanced_ads_ads_txt_init' );

View File

@@ -0,0 +1,172 @@
<?php // phpcs:ignore WordPress.Files.FileName
use AdvancedAds\Framework\Utilities\Params;
/**
* Display the 'ads.txt' file.
*/
class Advanced_Ads_Ads_Txt_Public {
const TOP = '# Advanced Ads ads.txt';
/**
* Ads.txt data management class
*
* @var Advanced_Ads_Ads_Txt_Strategy
*/
private $strategy;
/**
* The Constructor.
*
* @param Advanced_Ads_Ads_Txt_Strategy $strategy Ads.txt data management class.
*/
public function __construct( $strategy ) {
$this->strategy = $strategy;
add_action( 'init', [ $this, 'display' ] );
}
/**
* Display the 'ads.txt' file on the frontend.
*/
public function display() {
$request_uri = filter_var( $_SERVER['REQUEST_URI'] ?? '', FILTER_SANITIZE_URL );
if ( '/ads.txt' === esc_url_raw( $request_uri ) ) {
$content = $this->prepare_frontend_output();
if ( $content ) {
header( 'Content-Type: text/plain; charset=utf-8' );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $content;
exit;
}
}
}
/**
* Prepare frontend output.
*
* @return string
*/
public function prepare_frontend_output() {
if (
Advanced_Ads_Ads_Txt_Utils::is_subdir() ||
! $this->strategy->is_enabled()
) {
return;
}
$content = $this->get_frontend_output();
$content = $content ? self::TOP . "\n" . $content : '';
return $content;
}
/**
* Get output for frontend.
*
* @return string
*/
public function get_frontend_output() {
if ( $this->strategy->is_all_network() ) {
$content = $this->prepare_multisite();
} else {
$options = $this->strategy->get_options();
$content = $this->strategy->parse_content( $options );
$content = apply_filters( 'advanced-ads-ads-txt-content', $content, get_current_blog_id() );
}
return $content;
}
/**
* Prepare content of several blogs for output.
*
* @param string $domain Domain name.
* @return string
*/
public function prepare_multisite( $domain = null ) {
global $current_blog, $wpdb;
$domain = $domain ? $domain : $current_blog->domain;
$need_file_on_root_domain = Advanced_Ads_Ads_Txt_Utils::need_file_on_root_domain();
// Get all sites that include the current domain as part of their domains.
$sites = get_sites(
[
'search' => $domain,
'search_columns' => [ 'domain' ],
'meta_key' => Advanced_Ads_Ads_Txt_Strategy::OPTION, // phpcs:ignore
]
);
// Uses `subdomain=` variable.
$referrals = [];
// Included to the ads.txt file of the current domain.
$not_refferals = [];
foreach ( $sites as $site ) {
if ( get_current_blog_id() === (int) $site->blog_id ) {
// Current domain, no need to refer.
$not_refferals[] = $site->blog_id;
continue;
}
if ( $need_file_on_root_domain ) {
// Subdomains cannot refer to other subdomains.
$not_refferals[] = $site->blog_id;
continue;
}
if ( '/' !== $site->path ) {
// We can refer to domains, not domains plus path.
$not_refferals[] = $site->blog_id;
continue;
}
$referrals[ $site->blog_id ] = $site->domain;
}
$o = '';
if ( $not_refferals ) {
$results = $wpdb->get_results( // phpcs:ignore
sprintf(
"SELECT blog_id, meta_value FROM $wpdb->blogmeta WHERE meta_key='%s' AND blog_id IN (%s)",
Advanced_Ads_Ads_Txt_Strategy::OPTION, // phpcs:ignore
join( ',', array_map( 'absint', $not_refferals ) ) // phpcs:ignore
)
);
$blog_data = [];
foreach ( $results as $result ) {
$blog_id = $result->blog_id;
$options = maybe_unserialize( $result->meta_value );
$options = $this->strategy->load_default_options( $options );
$blog_data[ $blog_id ] = $options;
}
$blog_data = Advanced_Ads_Ads_Txt_Utils::remove_duplicate_lines( $blog_data, [ 'to_comments' => true ] );
foreach ( $blog_data as $blog_id => $blog_lines ) {
$content = $this->strategy->parse_content( $blog_lines );
if ( $content ) {
$content = "# blog_id: $blog_id\n" . $content;
}
if ( get_current_blog_id() === $blog_id ) {
// Refer to other subdomains.
foreach ( $referrals as $blog_id => $referral ) {
$content .= "# refer to blog_id: $blog_id\nsubdomain=" . $referral . "\n";
}
}
$content = apply_filters( 'advanced-ads-ads-txt-content', $content, $blog_id );
$o .= $content . "\n";
}
}
return $o;
}
}

View File

@@ -0,0 +1,282 @@
<?php // phpcs:ignoreFile
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Conditional;
/**
* Class Advanced_Ads_AdSense_Admin
*/
class Advanced_Ads_AdSense_Admin {
/**
* AdSense options.
*
* @var Advanced_Ads_AdSense_Data
*/
private $data;
/**
* Noncetodo: check if this is still used
* todo: check if this is still used
*
* @var string $nonce
*/
private $nonce;
/**
* Instance of Advanced_Ads_AdSense_Admin
*
* @var null
*/
private static $instance = null;
/**
* Notices
* todo: still used?
*
* @var null
*/
protected $notice = null;
/**
* Settings page hook
*
* @var string
*/
private $settings_page_hook = 'advanced-ads-adsense-settings-page';
const ADSENSE_NEW_ACCOUNT_LINK = 'https://www.google.com/adsense/start/?utm_source=AdvancedAdsPlugIn&utm_medium=partnerships&utm_campaign=AdvancedAdsPartner1';
/**
* Advanced_Ads_AdSense_Admin constructor.
*/
private function __construct() {
$this->data = Advanced_Ads_AdSense_Data::get_instance();
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'admin_print_scripts', [ $this, 'print_scripts' ] );
add_filter( 'advanced-ads-ad-notices', [ $this, 'ad_notices' ], 10, 2 );
add_action( 'advanced-ads-ad-pre-save', [ $this, 'save_ad_options' ], 10, 2 );
}
/**
* Save ad options.
*
* @param Ad $ad Ad instance.
* @param array $post_data Post data array.
*
* @return void
*/
public function save_ad_options( Ad $ad, $post_data ): void {
if ( ! $ad->is_type( 'adsense' ) ) {
return;
}
// Remove ad size options for responsive AdSense ads.
$content = json_decode( str_replace( "\n", '', wp_unslash( $post_data['content'] ) ), true );
if ( in_array( $content['unitType'] ?? 'none', [
'responsive',
'link',
'link-responsive',
'matched-content',
'in-article',
'in-feed',
], true )
) {
$ad->set_width( 0 );
$ad->set_height( 0 );
}
}
/**
* Load JavaScript needed on some pages.
*/
public function print_scripts() {
global $pagenow;
if ( $this->is_on_screen() ) {
$db = Advanced_Ads_AdSense_Data::get_instance();
$pub_id = $db->get_adsense_id();
?>
<script type="text/javascript">
if ( 'undefined' == typeof gadsenseData ) {
window.gadsenseData = {};
}
// todo: check why we are using echo here.
gadsenseData['pagenow'] = '<?php echo esc_attr( $pagenow ); ?>';
</script>
<?php
}
}
/**
* Add AdSense-related scripts.
*/
public function enqueue_scripts() {
if ( Conditional::is_screen_advanced_ads() ) {
self::enqueue_connect_adsense();
}
if ( $this->is_on_screen() ) {
$scripts = [];
// Allow modifications of script files to enqueue.
$scripts = apply_filters( 'advanced-ads-gadsense-ad-param-script', $scripts );
foreach ( $scripts as $handle => $value ) {
if ( empty( $handle ) ) {
continue;
}
if ( ! empty( $handle ) && empty( $value ) ) {
// Allow inclusion of WordPress's built-in script like jQuery.
wp_enqueue_script( $handle );
} else {
if ( ! isset( $value['version'] ) ) {
$value['version'] = null; }
wp_enqueue_script( $handle, $value['path'], $value['dep'], $value['version'] );
}
}
$styles = [];
// Allow modifications of default style files to enqueue.
$styles = apply_filters( 'advanced-ads-gadsense-ad-param-style', $styles );
foreach ( $styles as $handle => $value ) {
if ( ! isset( $value['path'] ) ||
! isset( $value['dep'] ) ||
empty( $handle )
) {
continue;
}
if ( ! isset( $value['version'] ) ) {
$value['version'] = null; }
wp_enqueue_style( $handle, $value['path'], $value['dep'], $value['version'] );
}
}
}
/**
* Get instance of Advanced_Ads_AdSense_Admin.
*
* @return Advanced_Ads_AdSense_Admin|null
*/
public static function get_instance() {
if ( null == self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Show AdSense ad specific notices in parameters box
*
* @param array $notices some notices to show in the parameters box.
* @param string $box ID of the meta box.
*/
public function ad_notices( $notices, $box ) {
switch ( $box['id'] ) {
case 'ad-parameters-box':
// Add warning if this is a responsive ad unit without custom sizes and position is set to left or right.
// Hidden by default and made visible with JS.
$notices[] = [
'text' => sprintf(
/* translators: %s is a URL. */
__( 'Responsive AdSense ads dont work reliably with <em>Position</em> set to left or right. Either switch the <em>Type</em> to "normal" or follow <a href="%s" target="_blank">this tutorial</a> if you want the ad to be wrapped in text.', 'advanced-ads' ),
'https://wpadvancedads.com/adsense-responsive-custom-sizes/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adsense-custom-sizes-tutorial'
),
'class' => 'advads-ad-notice-responsive-position advads-notice-inline advads-error hidden',
];
// Show hint about AdSense In-feed add-on.
if ( ! class_exists( 'Advanced_Ads_In_Feed', false ) && ! class_exists( 'Advanced_Ads_Pro_Admin', false ) ) {
$notices[] = [
'text' => sprintf(
/* translators: %s is a URL. */
__( '<a href="%s" target="_blank">Install the free AdSense In-feed add-on</a> in order to place ads between posts.', 'advanced-ads' ),
wp_nonce_url(
self_admin_url( 'update.php?action=install-plugin&plugin=advanced-ads-adsense-in-feed' ),
'install-plugin_advanced-ads-adsense-in-feed'
)
),
'class' => 'advads-ad-notice-in-feed-add-on advads-notice-inline advads-idea hidden',
];
}
break;
}
return $notices;
}
/**
* Enqueue AdSense connection script.
*/
public static function enqueue_connect_adsense() {
if ( ! wp_script_is( 'advads/connect-adsense', 'registered' ) ) {
wp_enqueue_script( 'advads/connect-adsense', GADSENSE_BASE_URL . 'admin/assets/js/connect-adsense.js', [ 'jquery' ], ADVADS_VERSION );
}
if ( ! has_action( 'admin_footer', [ 'Advanced_Ads_AdSense_Admin', 'print_connect_adsense' ] ) ) {
add_action( 'admin_footer', [ 'Advanced_Ads_AdSense_Admin', 'print_connect_adsense' ] );
}
}
/**
* Prints AdSense connection markup.
*/
public static function print_connect_adsense() {
require_once GADSENSE_BASE_PATH . 'admin/views/connect-adsense.php';
}
/**
* Get Auto Ads messages.
*/
public static function get_auto_ads_messages() {
return [
'enabled' => sprintf(
/* translators: %s is a URL. */
__( 'The AdSense verification and Auto ads code is already activated in the <a href="%s">AdSense settings</a>.', 'advanced-ads' ),
admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' )
)
. ' ' . __( 'No need to add the code manually here, unless you want to include it into certain pages only.', 'advanced-ads' ),
'disabled' => sprintf(
'%s <button id="adsense_enable_pla" type="button" class="button">%s</button>',
sprintf(
/* translators: %s is a URL. */
__( 'The AdSense verification and Auto ads code should be set up in the <a href="%s">AdSense settings</a>. Click on the following button to enable it now.', 'advanced-ads' ),
admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' )
),
esc_attr__( 'Activate', 'advanced-ads' )
),
];
}
/**
* Get the ad selector markup
*
* @param bool $hide_idle_ads Whether to hide idle ads.
*/
public static function get_mapi_ad_selector( $hide_idle_ads = true ) {
global $closeable, $use_dashicons, $network, $ad_units, $display_slot_id;
$closeable = true;
$use_dashicons = false;
$network = Advanced_Ads_Network_Adsense::get_instance();
$ad_units = $network->get_external_ad_units();
$display_slot_id = true;
$pub_id = Advanced_Ads_AdSense_Data::get_instance()->get_adsense_id();
require_once GADSENSE_BASE_PATH . 'admin/views/external-ads-list.php';
}
/**
* Is on screen.
*
* @return boolean
*/
private function is_on_screen(): bool {
global $pagenow, $post_type;
return ( 'post-new.php' === $pagenow && Constants::POST_TYPE_AD === $post_type ) ||
( 'post.php' === $pagenow && Constants::POST_TYPE_AD === $post_type && 'edit' === Params::get( 'action' ) );
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" overflow="visible"><path fill="#fff" d="M19.878 99C9.469 99 1 90.532 1 80.123V2a1 1 0 0 1 1-1h78.122C90.531 1 99 9.469 99 19.878V98a1 1 0 0 1-1 1H19.878z"/><path d="M80.122 2C89.98 2 98 10.02 98 19.878V98H19.878C10.02 98 2 89.981 2 80.123V2h78.122m0-2H2a2 2 0 0 0-2 2v78.123C0 91.083 8.917 100 19.878 100H98a2 2 0 0 0 2-2V19.878C100 8.917 91.083 0 80.122 0h0zm5.326 17.555H14.635a2.961 2.961 0 1 1 0-5.921h70.813a2.961 2.961 0 1 1 0 5.921zm0 70.812H14.635a2.961 2.961 0 1 1 0-5.921h70.813a2.961 2.961 0 1 1 0 5.921z" fill="#0074a2"/><path fill="#39f" d="M73.604 76.524H26.478a2.96 2.96 0 0 1-2.961-2.961V26.438a2.96 2.96 0 0 1 2.961-2.961h47.126a2.96 2.96 0 0 1 2.961 2.961v47.126c-.001 1.634-1.326 2.96-2.961 2.96z"/><path fill="#add6ff" d="M55.313 42.02c1.806-3.091.733-7.041-2.396-8.825s-7.131-.727-8.937 2.364l-.225.423-6.105 10.445c-.136.208-.261.42-.375.642l-6.341 10.94 11.331 6.352 6.309-10.848a6.75 6.75 0 0 0 .374-.642l6.106-10.447.259-.404"/><path fill="#fff" d="M42.319 64.327c-1.797 3.141-5.848 4.297-8.957 2.485s-4.217-5.747-2.418-8.888 5.811-4.3 8.921-2.488 4.249 5.751 2.454 8.891"/><path fill="#d6ebff" d="M66.769 44.089a6.48 6.48 0 0 0-8.837 2.361l-6.468 11.175c-1.782 3.083-.73 7.023 2.35 8.806.007.003.012.007.018.011a6.48 6.48 0 0 0 8.839-2.362l6.468-11.175a6.45 6.45 0 0 0-2.355-8.807"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" overflow="visible"><path fill="#fff" d="M19.878 99C9.469 99 1 90.531 1 80.121V2a1 1 0 0 1 1-1h78.122C90.531 1 99 9.468 99 19.877V98a1 1 0 0 1-1 1H19.878z"/><path d="M80.122 2C89.98 2 98 10.02 98 19.877V98H19.878C10.02 98 2 89.979 2 80.121V2h78.122m0-2H2a2 2 0 0 0-2 2v78.121C0 91.082 8.917 100 19.878 100H98a2 2 0 0 0 2-2V19.877C100 8.917 91.083 0 80.122 0h0zm5.324 17.554H14.633a2.961 2.961 0 1 1 0-5.921h70.813a2.961 2.961 0 1 1 0 5.921zm0 70.813H14.633a2.962 2.962 0 0 1 0-5.923h70.813a2.96 2.96 0 0 1 2.961 2.962c0 1.634-1.326 2.961-2.961 2.961z" fill="#0074a2"/><path fill="#ffbf00" d="M85.444 76.522H14.633a2.96 2.96 0 0 1-2.96-2.961V26.436a2.96 2.96 0 0 1 2.96-2.96h70.812a2.96 2.96 0 0 1 2.961 2.96v47.125c-.001 1.636-1.327 2.961-2.962 2.961z"/><path fill="#ffe599" d="M54.384 37.389c1.49-2.549.606-5.804-1.973-7.276s-5.878-.599-7.367 1.949l-.184.35-5.034 8.611c-.113.17-.213.345-.309.526l-5.228 9.019 9.339 5.236 5.202-8.942c.112-.17.218-.348.31-.53l5.031-8.609.213-.334"/><path fill="#fff" d="M43.673 55.777c-1.48 2.59-4.819 3.54-7.384 2.047s-3.475-4.735-1.994-7.325 4.792-3.545 7.356-2.051 3.501 4.739 2.021 7.327"/><path fill="#fff2cc" d="M63.829 39.093a5.34 5.34 0 0 0-7.285 1.947l-5.333 9.212c-1.469 2.542-.601 5.79 1.94 7.259 2.562 1.477 5.822.607 7.3-1.939l5.329-9.212c1.469-2.539.602-5.791-1.94-7.259"/><path fill="#fff" d="M79.523 70.602h-58.97a2.96 2.96 0 1 1 0-5.922h58.97a2.96 2.96 0 1 1 0 5.922z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" overflow="visible"><path fill="#fff" d="M19.878 99C9.469 99 1 90.531 1 80.122V2a1 1 0 0 1 1-1h78.122C90.531 1 99 9.468 99 19.877V98a1 1 0 0 1-1 1H19.878z"/><g fill="#0074a2"><path d="M80.122 2C89.98 2 98 10.02 98 19.877V98H19.878C10.02 98 2 89.98 2 80.122V2h78.122m0-2H2a2 2 0 0 0-2 2v78.122C0 91.083 8.917 100 19.878 100H98a2 2 0 0 0 2-2V19.877C100 8.917 91.083 0 80.122 0h0z"/><use xlink:href="#B"/><use xlink:href="#B" y="11.843"/></g><path fill="#52cc52" d="M85.408 64.809H14.596c-1.57 0-2.845-1.273-2.845-2.844V38.278c0-1.571 1.274-2.845 2.845-2.845h70.812c1.57 0 2.844 1.274 2.844 2.845v23.687c0 1.57-1.273 2.844-2.844 2.844z"/><path d="M79.484 47.161h-35.28a2.961 2.961 0 1 1 0-5.921h35.28a2.961 2.961 0 1 1 0 5.921zm0 11.843h-35.28a2.961 2.961 0 1 1 0-5.922h35.28a2.961 2.961 0 1 1 0 5.922z" fill="#fff"/><g fill="#0074a2"><path d="M79.484 76.769H44.206a2.96 2.96 0 1 1 0-5.922h35.278a2.96 2.96 0 1 1 0 5.922z"/><use xlink:href="#B" y="71.057"/><path d="M32.858 20.581a5.99 5.99 0 0 1-5.99 5.987 5.99 5.99 0 0 1-5.987-5.987 5.99 5.99 0 0 1 5.987-5.988 5.99 5.99 0 0 1 5.99 5.988zm0 59.213a5.99 5.99 0 0 1-5.99 5.989 5.99 5.99 0 0 1-5.987-5.989 5.99 5.99 0 0 1 5.987-5.986 5.99 5.99 0 0 1 5.99 5.986z"/></g><path fill="#baebba" d="M28.688 46.717a2.74 2.74 0 0 0-1.023-3.767 2.81 2.81 0 0 0-3.812 1.01l-.096.18-2.604 4.457c-.058.088-.112.179-.16.273l-2.705 4.669 4.833 2.709 2.691-4.629c.061-.088.113-.18.161-.274l2.604-4.456.111-.172"/><path fill="#fff" d="M23.145 56.233c-.767 1.341-2.496 1.833-3.823 1.061s-1.798-2.451-1.032-3.792 2.481-1.836 3.81-1.062 1.81 2.454 1.045 3.793"/><path fill="#dcf5dc" d="M33.575 47.598c-1.317-.76-3.005-.309-3.771 1.008l-2.759 4.768a2.75 2.75 0 0 0 1.003 3.758c1.327.766 3.014.315 3.779-1.005l2.758-4.768a2.75 2.75 0 0 0-1.004-3.757"/><defs ><path id="B" d="M79.484 17.553H44.206a2.96 2.96 0 1 1 0-5.921h35.278a2.96 2.96 0 1 1 0 5.921z"/></defs></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" overflow="visible"><path fill="#fff" d="M19.878 99C9.469 99 1 90.531 1 80.122V2a1 1 0 0 1 1-1h78.122C90.531 1 99 9.468 99 19.877V98a1 1 0 0 1-1 1H19.878z"/><path d="M80.122 2C89.98 2 98 10.02 98 19.877V98H19.878C10.02 98 2 89.98 2 80.122V2h78.122m0-2H2a2 2 0 0 0-2 2v78.122C0 91.083 8.917 100 19.878 100H98a2 2 0 0 0 2-2V19.877C100 8.917 91.083 0 80.122 0h0zm-.599 17.556H20.559a2.961 2.961 0 1 1 0-5.921h58.965a2.96 2.96 0 0 1 2.962 2.961c-.001 1.634-1.327 2.96-2.963 2.96zm0 11.842H20.559a2.961 2.961 0 1 1 0-5.921h58.965a2.96 2.96 0 0 1 2.962 2.96c-.001 1.635-1.327 2.961-2.963 2.961z" fill="#0074a2"/><path fill="#d95757" d="M85.446 88.496H14.635c-1.57 0-2.844-1.273-2.844-2.845V38.28c0-1.57 1.273-2.844 2.844-2.844h70.812c1.57 0 2.844 1.274 2.844 2.844v47.372c-.001 1.571-1.274 2.844-2.845 2.844z"/><path fill="#fff" d="M79.523 53.083h-35.28a2.96 2.96 0 1 1 0-5.921h35.28a2.96 2.96 0 1 1 0 5.921z"/><path fill="#f0bcbc" d="M28.727 46.719a2.74 2.74 0 0 0-1.021-3.766c-1.337-.762-3.043-.311-3.813 1.009l-.095.18-2.605 4.458c-.058.087-.11.178-.16.274l-2.704 4.666 4.834 2.712 2.689-4.631a2.49 2.49 0 0 0 .161-.272l2.604-4.457.11-.173"/><path fill="#fff" d="M23.183 56.235c-.766 1.341-2.495 1.834-3.821 1.061s-1.799-2.45-1.031-3.792 2.479-1.836 3.807-1.061 1.811 2.453 1.045 3.792"/><path fill="#f7dddd" d="M33.615 47.6c-1.319-.759-3.005-.309-3.769 1.009l-2.762 4.768a2.75 2.75 0 0 0 1.003 3.758c1.327.765 3.015.315 3.78-1.004l2.759-4.768a2.75 2.75 0 0 0-1.006-3.757"/><path fill="#fff" d="M79.523 76.77h-35.28a2.96 2.96 0 0 1 0-5.923h35.28c1.636 0 2.962 1.326 2.962 2.962s-1.326 2.961-2.962 2.961z"/><path fill="#f0bcbc" d="M28.727 70.404a2.74 2.74 0 0 0-1.021-3.766 2.81 2.81 0 0 0-3.813 1.009l-.095.18-2.605 4.458a3.34 3.34 0 0 0-.16.272l-2.704 4.668 4.834 2.709 2.689-4.628c.059-.088.114-.179.161-.273l2.604-4.457.11-.172"/><path fill="#fff" d="M23.183 79.921c-.766 1.341-2.495 1.834-3.821 1.061s-1.799-2.451-1.031-3.791 2.479-1.836 3.807-1.062 1.811 2.453 1.045 3.792"/><path fill="#f7dddd" d="M33.615 71.286a2.76 2.76 0 0 0-3.769 1.008l-2.762 4.77a2.75 2.75 0 0 0 1.003 3.756c1.327.765 3.015.315 3.78-1.003l2.759-4.769c.76-1.315.309-2.997-1.006-3.758"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,928 @@
/**
* actually this method was moved inside the ad network class.
* however the responsive addon depends on it, so i made it global again.
* this makes it downward compatible (with an older version of responsive),
* but you should probably adjust the responsive plugin to make use of
* the static method (AdvancedAdsNetworkAdsense.gadsenseFormatAdContent)
*
* in case you come across a missing method originating from the deleted new-ad.js,
* please just make the methods static and create a wrapper function like the one below
*/
window.gadsenseFormatAdContent = function () {
AdvancedAdsNetworkAdsense.gadsenseFormatAdContent();
};
class AdvancedAdsNetworkAdsense extends AdvancedAdsAdNetwork {
constructor( codes ) {
super( 'adsense' );
this.name = 'adsense';
this.codes = codes;
this.parseCodeBtnClicked = false;
this.preventCloseAdSelector = false;
// this.adUnitName = null;
// the legacy code of gadsense executes a script inside a php template and will may not have been executed
// at this stage. the AdvancedAdsAdNetwork class already knows the publisher id, so we will overwrite
// the field in gadsenseData to be up to date at all times.
// TODO: the use of gadsenseData.pubId could be removed from this class in favor of this.vars.pubId
gadsenseData.pubId = this.vars.pubId;
}
/**
* Legacy method
*
* Format the post content field
*/
static gadsenseFormatAdContent() {
const slotId = jQuery( '#ad-parameters-box #unit-code' ).val();
const unitType = jQuery( '#ad-parameters-box #unit-type' ).val();
const publisherId = jQuery( '#advads-adsense-pub-id' ).val() ? jQuery( '#advads-adsense-pub-id' ).val() : gadsenseData.pubId;
let adContent = {
slotId: slotId,
unitType: unitType,
pubId: publisherId
};
if ( unitType === 'responsive' ) {
let resize = jQuery( '#ad-parameters-box #ad-resize-type' ).val();
if ( resize === '0' ) {
resize = 'auto';
}
adContent.resize = resize;
}
if ( unitType === 'in-feed' ) {
adContent.layout_key = jQuery( '#ad-parameters-box #ad-layout-key' ).val();
}
if ( typeof adContent.resize !== 'undefined' && adContent.resize !== 'auto' ) {
jQuery( document ).trigger( 'gadsenseFormatAdResponsive', [adContent] );
}
jQuery( document ).trigger( 'gadsenseFormatAdContent', [adContent] );
if ( typeof window.gadsenseAdContent !== 'undefined' ) {
adContent = window.gadsenseAdContent;
delete( window.gadsenseAdContent );
}
jQuery( '#advads-ad-content-adsense' ).val( JSON.stringify( adContent, false, 2 ) );
}
openSelector() {
}
closeAdSelector() {
if ( this.preventCloseAdSelector ) {
return;
}
AdvancedAdsAdmin.AdImporter.closeAdSelector();
}
getSelectedId() {
const pubId = gadsenseData.pubId || false;
const slotId = jQuery( '#unit-code' ).val().trim();
return pubId && slotId ? 'ca-' + pubId + ':' + slotId : null;
}
selectAdFromList( slotId ) {
this.preventCloseAdSelector = true;
this.onSelectAd( slotId );
AdvancedAdsAdmin.AdImporter.openExternalAdsList();
const report = Advanced_Ads_Adsense_Report_Helper.getReportObject();
if ( report ) {
report.filter = slotId.split( ':' )[1];
report.updateHtmlAttr();
report.refresh();
}
}
updateAdFromList( slotId ) {
this.getRemoteCode( slotId );
}
getRefreshAdsParameters() {
return {
nonce: AdsenseMAPI.nonce,
action: 'advanced_ads_get_ad_units_adsense',
account: gadsenseData.pubId,
inactive: ! this.hideIdle
};
}
onManualSetup() {
jQuery( '.advads-adsense-code' ).css( 'display', 'none' );
jQuery( '#remote-ad-unsupported-ad-type' ).css( 'display', 'none' );
this.undoReadOnly();
}
/**
* Parse the code of an adsense ad and adjust the GUI
* call it, when an ad unit was selected.
* returns the parsed obj or false, when the ad code could not be parsed
*/
processAdCode( code ) {
const parsed = this.parseAdContentFailsafe( code );
if ( parsed ) {
this.undoReadOnly();
this.setDetailsFromAdCode( parsed );
this.makeReadOnly();
jQuery( '#remote-ad-code-error' ).hide();
jQuery( '#remote-ad-unsupported-ad-type' ).hide();
this.closeAdSelector();
} else {
jQuery( '#remote-ad-code-error' ).show();
}
return parsed;
}
/**
* Clone of legacy method
*
* @param slotID
*/
onSelectAd( slotID ) {
if ( typeof this.codes[slotID] !== 'undefined' ) {
this.getSavedDetails( slotID );
} else {
this.getRemoteCode( slotID );
}
}
/**
* Legacy method
*
* @param slotID
*/
getSavedDetails( slotID ) {
if ( typeof this.codes[slotID] !== 'undefined' ) {
const parsed = this.processAdCode( this.codes[slotID] );
if ( parsed !== false ) {
jQuery( '#remote-ad-unsupported-ad-type' ).css( 'display', 'none' );
this.closeAdSelector();
this.preventCloseAdSelector = false;
}
}
}
/**
* Legacy method
*
* @param slotID
*/
getRemoteCode( slotID ) {
if ( slotID === '' ) {
return;
}
jQuery( '#mapi-loading-overlay' ).css( 'display', 'block' );
const self = this;
jQuery.ajax( {
type: 'post',
url: ajaxurl,
data: {
nonce: AdsenseMAPI.nonce,
action: 'advads_mapi_get_adCode',
unit: slotID
},
success: function ( response ) {
jQuery( '#mapi-loading-overlay' ).css( 'display', 'none' );
if ( typeof response.code !== 'undefined' ) {
jQuery( '#remote-ad-code-msg' ).empty();
if ( self.processAdCode( response.code ) !== false ) {
self.codes[slotID] = response.code;
AdvancedAdsAdmin.AdImporter.unitIsSupported( slotID );
}
AdvancedAdsAdmin.AdImporter.highlightSelectedRowInExternalAdsList();
jQuery( '[data-slotid="' + slotID + '"]' ).children( '.unittype' ).text( response.type );
self.closeAdSelector();
} else {
if ( typeof response.raw !== 'undefined' ) {
jQuery( '#remote-ad-code-msg' ).html( response.raw );
} else if ( typeof response.msg !== 'undefined' ) {
if ( typeof response.reload !== 'undefined' ) {
AdvancedAdsAdmin.AdImporter.emptyMapiSelector( response.msg );
} else {
if ( response.msg === 'doesNotSupportAdUnitType' ) {
AdvancedAdsAdmin.AdImporter.unitIsNotSupported( slotID );
} else {
jQuery( '#remote-ad-code-msg' ).html( response.msg );
}
}
if ( typeof response.raw !== 'undefined' ) {
console.log( response.raw );
}
}
}
},
error: function () {
jQuery( '#mapi-loading-overlay' ).css( 'display', 'none' );
}
} );
}
/**
* Legacy method
*
* Parse ad content.
*
* @return {!Object}
*/
parseAdContent( content ) {
const rawContent = typeof content !== 'undefined' ? content.trim() : '';
const theContent = jQuery( '<div />' ).html( rawContent );
const adByGoogle = theContent.find( 'ins' );
let theAd = {};
theAd.slotId = adByGoogle.attr( 'data-ad-slot' ) || '';
if ( typeof adByGoogle.attr( 'data-ad-client' ) !== 'undefined' ) {
theAd.pubId = adByGoogle.attr( 'data-ad-client' ).substr( 3 );
}
if ( theAd.slotId !== undefined && theAd.pubId !== '' ) {
theAd.display = adByGoogle.css( 'display' );
theAd.format = adByGoogle.attr( 'data-ad-format' );
theAd.layout = adByGoogle.attr( 'data-ad-layout' ); // for In-feed and In-article
theAd.layout_key = adByGoogle.attr( 'data-ad-layout-key' ); // for InFeed
theAd.style = adByGoogle.attr( 'style' ) || '';
// Normal ad.
if ( typeof theAd.format === 'undefined' && theAd.style.indexOf( 'width' ) !== - 1 ) {
theAd.type = 'normal';
theAd.width = adByGoogle.css( 'width' ).replace( 'px', '' );
theAd.height = adByGoogle.css( 'height' ).replace( 'px', '' );
} else if ( typeof theAd.format !== 'undefined' && theAd.format === 'auto' ) {
// Responsive ad, auto resize.
theAd.type = 'responsive';
} else if ( typeof theAd.format !== 'undefined' && theAd.format === 'link' ) {
// Older link unit format; for new ads the format type is no longer needed; link units are created through the AdSense panel
if ( theAd.style.indexOf( 'width' ) !== - 1 ) {
// Is fixed size.
theAd.width = adByGoogle.css( 'width' ).replace( 'px', '' );
theAd.height = adByGoogle.css( 'height' ).replace( 'px', '' );
theAd.type = 'link';
} else {
// Is responsive.
theAd.type = 'link-responsive';
}
} else if ( typeof theAd.format !== 'undefined' && theAd.format === 'autorelaxed' ) {
// Responsive Matched Content
theAd.type = 'matched-content';
} else if ( typeof theAd.format !== 'undefined' && theAd.format === 'fluid' ) {
// In-article & In-feed ads.
if ( typeof theAd.layout !== 'undefined' && theAd.layout === 'in-article' ) {
// In-article.
theAd.type = 'in-article';
} else {
// In-feed.
theAd.type = 'in-feed';
}
}
}
/**
* Synchronous code
*/
if ( rawContent.indexOf( 'google_ad_slot' ) !== - 1 ) {
const adClient = rawContent.match( /google_ad_client ?= ?["']([^'"]+)/ );
const adSlot = rawContent.match( /google_ad_slot ?= ?["']([^'"]+)/ );
const adFormat = rawContent.match( /google_ad_format ?= ?["']([^'"]+)/ );
const adWidth = rawContent.match( /google_ad_width ?= ?([\d]+)/ );
const adHeight = rawContent.match( /google_ad_height ?= ?([\d]+)/ );
theAd = {};
theAd.pubId = adClient[1].substr( 3 );
if ( adSlot !== null ) {
theAd.slotId = adSlot[1];
}
if ( adFormat !== null ) {
theAd.format = adFormat[1];
}
if ( adWidth !== null ) {
theAd.width = parseInt( adWidth[1] );
}
if ( adHeight !== null ) {
theAd.height = parseInt( adHeight[1] );
}
if ( typeof theAd.format === 'undefined' ) {
theAd.type = 'normal';
}
}
if ( theAd.slotId === '' && gadsenseData.pubId && gadsenseData.pubId !== '' ) {
theAd.type = jQuery( '#unit-type' ).val();
}
// Page-Level ad.
if ( rawContent.indexOf( 'enable_page_level_ads' ) !== - 1 || /script[^>]+data-ad-client=/.test( rawContent ) ) {
theAd = {'parse_message': 'pageLevelAd'};
} else if ( ! theAd.type ) {
// Unknown ad.
theAd = {'parse_message': 'unknownAd'};
}
jQuery( document ).trigger( 'gadsenseParseAdContent', [theAd, adByGoogle] );
return theAd;
}
parseAdContentFailsafe( code ) {
if ( typeof code === 'string' ) {
try {
code = JSON.parse( code );
} catch ( e ) {
return this.parseAdContent( code );
}
}
return code;
}
/**
* Handle result of parsing content.
*
* Legacy method
*/
handleParseResult( parseResult ) {
jQuery( '#pastecode-msg' ).empty();
switch ( parseResult.parse_message ) {
case 'pageLevelAd' :
advads_show_adsense_auto_ads_warning();
break;
case 'unknownAd' :
// Not recognized ad code.
if ( this.parseCodeBtnClicked && 'post-new.php' === gadsenseData.pagenow ) {
// do not show if just after switching to AdSense ad type on ad creation.
jQuery( '#pastecode-msg' ).append( jQuery( '<p />' ).css( 'color', 'red' ).html( gadsenseData.msg.unknownAd ) );
}
break;
default:
this.setDetailsFromAdCode( parseResult );
if ( typeof AdsenseMAPI !== 'undefined' && typeof AdsenseMAPI.hasToken !== 'undefined' && AdsenseMAPI.pubId === parseResult.pubId ) {
const content = jQuery( '#advanced-ads-ad-parameters input[name="advanced_ad[content]"]' ).val();
this.mapiSaveAdCode( content, parseResult.slotId );
this.makeReadOnly();
}
jQuery( '.advads-adsense-code' ).hide();
jQuery( '.advads-adsense-show-code' ).show();
jQuery( '.mapi-insert-code' ).show();
const customInputs = this.getCustomInputs();
customInputs.show();
}
}
/**
* Legacy method
*
* Set ad parameters fields from the result of parsing ad code
*/
setDetailsFromAdCode( theAd ) {
this.undoReadOnly();
jQuery( '#unit-code' ).val( theAd.slotId );
jQuery( '#advads-adsense-pub-id' ).val( theAd.pubId );
if ( theAd.type === 'normal' ) {
jQuery( '#unit-type' ).val( 'normal' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( theAd.width );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( theAd.height );
} else if ( theAd.type === 'responsive' ) {
jQuery( '#unit-type' ).val( 'responsive' );
jQuery( '#ad-resize-type' ).val( 'auto' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( '' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( '' );
} else if ( theAd.type === 'link') {
jQuery( '#unit-type' ).val( 'link' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( theAd.width );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( theAd.height );
} else if ( theAd.type === 'link-responsive' ) {
jQuery( '#unit-type' ).val( 'link-responsive' );
jQuery( '#ad-resize-type' ).val( 'auto' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( '' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( '' );
} else if ( theAd.type === 'matched-content' ) {
jQuery( '#unit-type' ).val( 'matched-content' );
jQuery( '#ad-resize-type' ).val( 'auto' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( '' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( '' );
} else if ( theAd.type === 'in-article' ) {
jQuery( '#unit-type' ).val( 'in-article' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( '' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( '' );
} else if ( theAd.type === 'in-feed' ) {
jQuery( '#unit-type' ).val( 'in-feed' );
jQuery( '#ad-layout-key' ).val( theAd.layout_key );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val( '' );
jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val( '' );
}
const storedPubId = gadsenseData.pubId;
if ( storedPubId !== '' && theAd.pubId !== storedPubId && theAd.slotId !== '' ) {
jQuery( '#adsense-ad-param-error' ).text( gadsenseData.msg.pubIdMismatch );
} else {
jQuery( '#adsense-ad-param-error' ).empty();
}
jQuery( document ).trigger( 'this.setDetailsFromAdCode', [theAd] );
jQuery( '#unit-type' ).trigger( 'change' );
}
/**
* Legacy method
*/
updateAdsenseType() {
const $ = jQuery;
const type = $( '#unit-type' ).val();
const layout = $( '.advads-adsense-layout' );
const layoutKey = $( '.advads-adsense-layout-key' );
const size = $( '#advanced-ads-ad-parameters-size' );
const width = $( '[name="advanced_ad\[width\]"]' );
const height = $( '[name="advanced_ad\[height\]"]' );
layout.hide().next( 'div' ).hide();
layoutKey.hide().next( 'div' ).hide();
jQuery( '.advads-ad-notice-in-feed-add-on' ).hide();
if ( type === 'responsive' || type === 'link-responsive' || type === 'matched-content' ) {
size.hide().prev( '.label' ).hide();
size.next( '.hr' ).hide();
$( '.clearfix-before' ).show();
} else if ( type === 'in-feed' ) {
layout.hide().next( 'div' ).hide();
layoutKey.show().next( 'div' ).show();
size.hide().prev( '.label' ).hide();
size.next( '.hr' ).hide();
// show add-on notice
$( '.advads-ad-notice-in-feed-add-on' ).show();
$( '.clearfix-before' ).show();
} else if ( type === 'in-article' ) {
size.hide().prev( '.label' ).hide();
size.next( '.hr' ).hide();
$( '.clearfix-before' ).show();
} else if ( type === 'normal' || type === 'link' ) {
size.show().prev( '.label' ).show();
size.next( '.hr' ).show();
$( '.clearfix-before' ).hide();
if ( ! width.val() ) {
width.val( '300' );
}
if ( ! height.val() ) {
height.val( '250' );
}
}
$( document ).trigger( 'gadsenseUnitChanged' );
AdvancedAdsNetworkAdsense.gadsenseFormatAdContent();
this.show_float_warnings( type );
}
/**
* Legacy method
*
* Show / hide position warning.
*/
show_float_warnings( unit_type ) {
const resize_type = jQuery( '#ad-resize-type' ).val();
const position = jQuery( 'input[name="advanced_ad[output][position]"]:checked' ).val();
if (
( ['link-responsive', 'matched-content', 'in-article', 'in-feed'].indexOf( unit_type ) !== - 1
|| ( unit_type === 'responsive' && resize_type !== 'manual' )
)
&& ( position.startsWith( 'left' ) || position.startsWith( 'right' ) )
) {
jQuery( '#ad-parameters-box-notices .advads-ad-notice-responsive-position' ).show();
} else {
jQuery( '#ad-parameters-box-notices .advads-ad-notice-responsive-position' ).hide();
}
}
/**
* Legacy method - adds readonly to relevant inputs
*/
makeReadOnly() {
jQuery( '#unit-type option:not(:selected)' ).prop( 'disabled', true );
}
/**
* Legacy method - removes readonly from relevant inputs (original name getSlotAndType_jq)
*/
undoReadOnly() {
jQuery( '#unit-code,#ad-layout,#ad-layout-key,[name="advanced_ad[width]"],[name="advanced_ad[height]"]' ).prop( 'readonly', false );
jQuery( '#unit-type option:not(:selected)' ).prop( 'disabled', false );
}
getCustomInputs() {
const $div1 = jQuery( '#unit-code' ).closest( 'div' );
const $label1 = $div1.prev();
const $hr1 = $div1.next();
const $label2 = $hr1.next();
const $div2 = $label2.next();
const $layoutKey = jQuery( '#ad-layout-key' ).closest( 'div' );
const $layoutKeyLabel = $layoutKey.prev( '#advads-adsense-layout-key' );
return $div1.add( $label1 ).add( $hr1 ).add( $label2 ).add( $div2 ).add( $layoutKey ).add( $layoutKeyLabel );
}
onBlur() {
}
onSelected() {
// Handle a click from the "Switch to AdSense ad" button.
if ( AdvancedAdsAdmin.AdImporter.adsenseCode ) {
this.parseCodeBtnClicked = true;
const parseResult = this.parseAdContent( AdvancedAdsAdmin.AdImporter.adsenseCode );
AdvancedAdsAdmin.AdImporter.adsenseCode = null;
this.handleParseResult( parseResult );
} else {
// When you are not connected to adsense, or if the ad was edited manually open the manual setup view.
let switchToManualSetup = ! this.vars.connected;
if ( ! switchToManualSetup ) {
const parsedAd = this.parseAdContentFailsafe( this.codes[this.getSelectedId()] );
if ( parsedAd ) {
// We need to check if the type of the ad is different from the default. this marks a manually setup ad.
if ( parsedAd.type !== jQuery( '#unit-type' ).val() ) {
// This ad was manually setup. don't open the selector, but switch to manual select.
switchToManualSetup = true;
}
}
}
if ( switchToManualSetup ) {
AdvancedAdsAdmin.AdImporter.manualSetup();
} else if ( AdvancedAdsAdmin.AdImporter.highlightSelectedRowInExternalAdsList() || ! this.getSelectedId() ) {
AdvancedAdsAdmin.AdImporter.openExternalAdsList();
}
}
}
onDomReady() {
const self = this;
jQuery( document ).on( 'click', '.advads-adsense-close-code', function ( ev ) {
ev.preventDefault();
self.onSelected();
} );
jQuery( document ).on( 'click', '.advads-adsense-submit-code', function ( ev ) {
ev.preventDefault();
self.parseCodeBtnClicked = true;
const rawContent = jQuery( '.advads-adsense-content' ).val();
const parseResult = self.parseAdContent( rawContent );
self.handleParseResult( parseResult );
if ( AdvancedAdsAdmin.AdImporter.highlightSelectedRowInExternalAdsList() ) {
AdvancedAdsAdmin.AdImporter.openExternalAdsList();
self.preventCloseAdSelector = true;
// save the manually added ad code to the AdSense settings
wp.ajax.post( 'advads-mapi-save-manual-code', {
raw_code: encodeURIComponent( rawContent ),
parsed_code: parseResult,
nonce: AdsenseMAPI.nonce
} )
.fail( function ( r ) {
const $notice = jQuery( '<div>' ).addClass( 'notice notice-error' ).html( jQuery( '<p>' ).text( r.responseJSON.data.message ) );
jQuery( '#post' ).before( $notice );
jQuery( 'body html' ).animate(
{
scrollTop: $notice.offset().top
},
200
);
} );
} else {
// No adsense ad with this slot id was found.
// Switches to manual ad setup view.
self.preventCloseAdSelector = false;
AdvancedAdsAdmin.AdImporter.manualSetup();
}
} );
jQuery( document ).on( 'gadsenseUnitChanged', function () {
const $row = jQuery( 'tr[data-slotid$="' + jQuery( '#unit-code' ).val() + '"]' );
let type = window.adsenseAdvancedAdsJS.ad_types.display;
switch ( jQuery( '#unit-type' ).val() ) {
case 'matched-content':
type = window.adsenseAdvancedAdsJS.ad_types.matched_content;
break;
case 'link':
case 'link-responsive':
type = window.adsenseAdvancedAdsJS.ad_types.link;
break;
case 'in-article':
type = window.adsenseAdvancedAdsJS.ad_types.in_article;
break;
case 'in-feed':
type = window.adsenseAdvancedAdsJS.ad_types.in_feed;
break;
}
$row.children( '.unittype' ).text( type );
} );
jQuery( document ).on( 'change', '#unit-type, #unit-code, #ad-layout-key', function () {
self.checkAdSlotId( this );
} );
const inputCode = jQuery( '#unit-code' );
if ( inputCode ) {
this.checkAdSlotId( inputCode[0] );
}
jQuery( document ).on( 'change', '#ad-resize-type', function () {
self.show_float_warnings( 'responsive' );
} );
this.updateAdsenseType();
if ( typeof AdsenseMAPI.hasToken !== 'undefined' ) {
this.mapiMayBeSaveAdCode();
}
jQuery( document ).on( 'click', '#mapi-archived-ads', function () {
self.showArchivedAds( jQuery( this ).hasClass( 'dashicons-visibility' ) );
} );
jQuery( '#wpwrap' ).on(
'advads-mapi-adlist-opened',
function () {
// Update ad unit list to v2 data the first time the ad list is opened.
if ( jQuery( '#mapi-force-v2-list-update' ).length ) {
jQuery( '#mapi-wrap i[data-mapiaction="updateList"]' ).trigger( 'click' );
return;
}
self.showArchivedAds();
}
);
this.onSelected();
}
showArchivedAds( show ) {
if ( typeof show === 'undefined' ) {
show = false;
}
const icon = jQuery( '#mapi-archived-ads' );
const title = icon.attr( 'title' );
const altTitle = icon.attr( 'data-alt-title' );
if ( show ) {
jQuery( '#mapi-table-wrap tbody tr[data-archived="1"]' ).show();
icon.removeClass( 'dashicons-visibility' ).addClass( 'dashicons-hidden' ).attr( 'title', altTitle ).attr( 'data-alt-title', title );
} else {
jQuery( '#mapi-table-wrap tbody tr[data-archived="1"]' ).not( '.selected' ).hide();
icon.removeClass( 'dashicons-hidden' ).addClass( 'dashicons-visibility' ).attr( 'title', altTitle ).attr( 'data-alt-title', title );
}
}
checkAdSlotId( elm ) {
if ( jQuery( elm ).attr( 'id' ) === 'unit-code' ) {
let val = jQuery( elm ).val();
if ( val ) {
val = val.trim();
}
if ( val.length > 0 && gadsenseData.pubId && val.indexOf( gadsenseData.pubId.substr( 4 ) ) !== -1 ) {
jQuery( '#advads-pubid-in-slot' ).css( 'display', 'block' );
jQuery( elm ).css( 'background-color', 'rgba(255, 235, 59, 0.33)' );
this.updateAdsenseType();
return;
}
}
jQuery( '#unit-code' ).css( 'background-color', '#fff' );
jQuery( '#advads-pubid-in-slot' ).css( 'display', 'none' );
this.updateAdsenseType();
}
mapiSaveAdCode( code, slot ) {
if ( typeof AdsenseMAPI.hasToken !== 'undefined' && typeof this.codes['ca-' + AdsenseMAPI.pubId + ':' + slot] === 'undefined' ) {
this.codes['ca-' + AdsenseMAPI.pubId + ':' + slot] = code;
jQuery( '#mapi-loading-overlay' ).css( 'display', 'block' );
jQuery.ajax( {
type: 'post',
url: ajaxurl,
data: {
nonce: AdsenseMAPI.nonce,
slot: slot,
code: code,
action: 'advads-mapi-reconstructed-code'
},
success: function ( resp, status, XHR ) {
jQuery( '#mapi-loading-overlay' ).css( 'display', 'none' );
},
error: function ( req, status, err ) {
jQuery( '#mapi-loading-overlay' ).css( 'display', 'none' );
}
} );
}
}
mapiMayBeSaveAdCode() {
// MAPI not set up
if ( typeof AdsenseMAPI.hasToken === 'undefined' ) {
return;
}
const slotId = jQuery( '#unit-code' ).val();
if ( ! slotId ) {
return;
}
const type = jQuery( '#unit-type' ).val();
const width = jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[width]"]' ).val().trim();
const height = jQuery( '#advanced-ads-ad-parameters-size input[name="advanced_ad[height]"]' ).val().trim();
const layout = jQuery( '#ad-layout' ).val();
const layoutKey = jQuery( '#ad-layout-key' ).val();
let code = false;
switch ( type ) {
case 'in-feed':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:block;" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '" ' +
'data-ad-layout-key="' + layoutKey + '" ';
code += 'data-ad-format="fluid"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
case 'in-article':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:block;text-align:center;" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '" ' +
'data-ad-layout="in-article" ' +
'data-ad-format="fluid"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
case 'matched-content':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:block;" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '" ' +
'data-ad-format="autorelaxed"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
case 'link-responsive':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:block;" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '" ' +
'data-ad-format="link"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
case 'link':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:block;width:' + width + 'px;height:' + height + 'px" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '" ' +
'data-ad-format="link"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
case 'responsive':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:block;" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '" ' +
'data-ad-format="auto"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
case 'normal':
code = '<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>' +
'<ins class="adsbygoogle" ' +
'style="display:inline-block;width:' + width + 'px;height:' + height + 'px" ' +
'data-ad-client="ca-' + AdsenseMAPI.pubId + '" ' +
'data-ad-slot="' + slotId + '"></ins>' +
'<script>' +
'(adsbygoogle = window.adsbygoogle || []).push({});' +
'</script>';
break;
default:
}
if ( code ) {
this.mapiSaveAdCode( code, slotId );
}
}
getMapiAction( action ) {
const self = this;
if ( action === 'toggleidle' ) {
return function ( ev, el ) {
self.hideIdle = ! self.hideIdle;
AdvancedAdsAdmin.AdImporter.refreshAds();
};
}
return null;
}
}
// Creates a Advanced_Ads_Adsense_Report_UI instance on the fly
window.Advanced_Ads_Adsense_Report_Helper = window.Advanced_Ads_Adsense_Report_Helper || {};
window.Advanced_Ads_Adsense_Report_Helper.init = function ( element ) {
if ( jQuery( element ).attr( 'data-arguments' ) ) {
try {
const reportArgs = JSON.parse( jQuery( element ).attr( 'data-arguments' ) );
jQuery( element ).data( 'advadsAdsenseReport', new Advanced_Ads_Adsense_Report_UI( element, reportArgs ) );
} catch ( ex ) {
console.error( 'Cannot parse report arguments' );
}
}
};
window.Advanced_Ads_Adsense_Report_Helper.getReportObject = function () {
const reportElem = jQuery( '.advanced-ads-adsense-dashboard' );
if ( reportElem.length ) {
let report = reportElem.data( 'advadsAdsenseReport' );
if ( typeof report.refresh === 'function' ) {
return report;
}
}
return false;
};
class Advanced_Ads_Adsense_Report_UI {
constructor( el, args ) {
this.$el = jQuery( el );
this.type = args.type;
this.filter = args.filter;
this.init();
this.refreshing = false;
}
// Update arguments attributes before refreshing.
updateHtmlAttr() {
this.$el.attr( 'data-arguments', JSON.stringify( {type: 'domain', filter: self.filter} ) );
}
// Get markup for the current arguments.
refresh() {
const self = this;
this.$el.html( '<p style="text-align:center;"><span class="report-need-refresh spinner advads-ad-parameters-spinner advads-spinner"></span></p>' );
jQuery.ajax( {
type: 'POST',
url: ajaxurl,
data: {
nonce: window.Advanced_Ads_Adsense_Report_Helper.nonce,
type: this.type,
filter: this.filter,
action: 'advads_adsense_report_refresh'
},
success: function ( response ) {
if ( response.success && response.data && response.data.html ) {
self.$el.html( response.data.html );
}
}, error: function ( request, status, error ) {
console.log( 'Refreshing rerpot error: ' + error );
}
} );
}
// Initialization - events binding.
init() {
if ( this.$el.find( '.report-need-refresh' ).length ) {
this.refresh();
}
const self = this;
// Hide dropdown on click on everything but the dropdown and its children.
jQuery( document ).on( 'click', '#wpwrap', function () {
const dd = jQuery( '#advads_overview_adsense_stats .advads-stats-dd-button .advads-stats-dd-items' );
if ( dd.is( ':visible' ) ) {
dd.hide();
}
} );
// Show the dropdown.
jQuery( document ).on( 'click', '#advads_overview_adsense_stats .advads-stats-dd-button', function ( ev ) {
// Stop bubbling. Prevents hiding the dropdown.
ev.stopPropagation();
const dd = jQuery( this ).find( '.advads-stats-dd-items' );
if ( ! dd.is( ':visible' ) ) {
dd.show();
}
} );
// Dropdown item clicked.
jQuery( document ).on( 'click', '.advads-stats-dd-button .advads-stats-dd-item', function () {
self.filter = jQuery( this ).attr( 'data-domain' );
self.updateHtmlAttr();
self.refresh();
} );
}
}

View File

@@ -0,0 +1,296 @@
/* eslint-disable no-console */
(function ($) {
// Unique instance of "advadsMapiConnectClass"
let INSTANCE = null;
const advadsMapiConnectClass = function (content, options) {
this.options = {};
this.modal = $('#gadsense-modal');
this.frame = null;
if ('undefined' === typeof content || !content) {
content = 'confirm-code';
}
this.setOptions(options);
this.init();
this.show(content);
return this;
};
advadsMapiConnectClass.prototype = {
constructor: advadsMapiConnectClass,
// Set options, for re-use of existing instance for a different purpose.
setOptions(options) {
const defaultOptions = {
onSuccess: false,
onError: false,
};
if ('undefined' === typeof options) {
options = defaultOptions;
}
this.options = $.extend({}, defaultOptions, options);
},
// Tasks to do after a successful connection.
exit() {
if (this.options.onSuccess) {
if ('function' === typeof this.options.onSuccess) {
this.options.onSuccess(this);
}
} else {
this.hide();
}
},
// Submit OAuth2 code for account connection.
submitOAuthCode(code) {
const self = this;
if (!code) {
return;
}
const overlay = $('.gadsense-overlay').css('display', 'block'),
codeInput = $('mapi-code');
$('#gadsense-modal-error').hide();
$.ajax({
url: ajaxurl,
type: 'post',
data: {
action: 'advads_gadsense_mapi_confirm_code',
code,
nonce: AdsenseMAPI.nonce,
},
success(response) {
codeInput.val('');
if (
response.data.status &&
true === response.data.status &&
response.data.token_data
) {
self.getAccountDetails(response.data.token_data);
} else {
// Connection error handling.
console.error(response);
overlay.css('display', 'none');
codeInput.val('');
$(
'#gadsense-modal-content-inner .dashicons-dismiss'
).trigger('click');
}
},
error() {
$('#gadsense-loading-overlay').css('display', 'none');
},
});
},
// Initialization - mostly binding events.
init() {
const that = this;
// Close the modal and hide errors.
$(document).on(
'click',
'#gadsense-modal .dashicons-dismiss',
function () {
that.hide();
}
);
// Account selection
$(document).on(
'click',
'.gadsense-modal-content-inner[data-content="account-selector"] button',
function () {
const adsenseID = $('#mapi-select-account').val();
let tokenData = false;
const tokenString = $(
'.gadsense-modal-content-inner[data-content="account-selector"] input.token-data'
).val();
let details = false;
const detailsString = $(
'.gadsense-modal-content-inner[data-content="account-selector"] input.accounts-details'
).val();
try {
tokenData = JSON.parse(tokenString);
} catch (Ex) {
console.error('Bad token data : ' + tokenString);
}
try {
details = JSON.parse(detailsString);
} catch (Ex) {
console.error('Bad account details : ' + detailsString);
}
if (details) {
$('.gadsense-overlay').css('display', 'block');
$.ajax({
url: ajaxurl,
type: 'post',
data: {
action: 'advads_gadsense_mapi_select_account',
nonce: AdsenseMAPI.nonce,
account: details[adsenseID],
token_data: tokenData,
},
success(response) {
if (
response.data.status &&
true === response.data.status
) {
INSTANCE.exit();
} else {
console.log(response);
}
},
error() {
$('#gadsense-loading-overlay').css(
'display',
'none'
);
},
});
}
}
);
},
// Get account info based on a newly obtained token.
getAccountDetails(tokenData) {
const data = {
action: 'advads_gadsense_mapi_get_details',
nonce: AdsenseMAPI.nonce,
};
data.token_data = tokenData;
$.ajax({
url: ajaxurl,
type: 'post',
data,
success(response) {
if (response.success && true === response.success) {
if (response.data && response.data.reload) {
INSTANCE.exit();
} else if (response.data && response.data.token_data) {
INSTANCE.switchContent('account-selector');
INSTANCE.frame
.find('select')
.html(response.data.html);
INSTANCE.frame
.find('input.token-data')
.val(JSON.stringify(response.data.token_data));
INSTANCE.frame
.find('input.accounts-details')
.val(JSON.stringify(response.data.details));
} else {
INSTANCE.switchContent('error');
INSTANCE.frame
.find('.error-message')
.text(JSON.stringify(response));
}
}
},
error(request) {
if (request.responseJSON) {
if (request.responseJSON.data.error) {
INSTANCE.switchContent('error');
INSTANCE.frame
.find('.error-message')
.text(request.responseJSON.data.error);
if (
typeof AdsenseMAPI.connectErrorMsg[
request.responseJSON.data.error
] !== 'undefined'
) {
INSTANCE.frame
.find('.error-description')
.html(
AdsenseMAPI.connectErrorMsg[
request.responseJSON.data.error
]
);
}
} else if (request.responseJSON.data.message) {
INSTANCE.frame
.find('.error-message')
.text(request.responseJSON.data.message);
}
return;
}
$('#gadsense-loading-overlay').css('display', 'none');
},
});
},
// Switch between frames in the modal container.
switchContent(content) {
if (
this.modal.find(
'.gadsense-modal-content-inner[data-content="' +
content +
'"]'
).length
) {
this.modal
.find('.gadsense-modal-content-inner')
.css('display', 'none');
this.frame = this.modal.find(
'.gadsense-modal-content-inner[data-content="' +
content +
'"]'
);
this.frame.css('display', 'block');
$('.gadsense-overlay').css('display', 'none');
}
},
// Show the modal frame with a given content.
show(content) {
if ('undefined' === typeof content) {
content = 'confirm-code';
}
this.switchContent(content);
if ('open-google' === content) {
window.location.href = AdsenseMAPI.oAuth2;
} else {
this.modal.css('display', 'block');
}
},
// Hide the modal frame
hide() {
window.location.href = this.modal.attr('data-return');
},
};
window.advadsMapiConnectClass = advadsMapiConnectClass;
// Shortcut function.
window.advadsMapiConnect = function (content, options) {
if ('undefined' === typeof content || !content) {
content = 'confirm-code';
}
if ('undefined' === typeof options) {
options = {};
}
if (null === INSTANCE) {
INSTANCE = new advadsMapiConnectClass(content, options);
} else {
INSTANCE.show(content, options);
}
};
$(function () {
// Move the the pop-up outside of any form.
$('#wpwrap').append($('#gadsense-modal'));
if ($('#advads-adsense-oauth-code').length) {
INSTANCE = new advadsMapiConnectClass('confirm-code', {});
INSTANCE.submitOAuthCode($('#advads-adsense-oauth-code').val());
}
});
})(window.jQuery);

View File

@@ -0,0 +1,139 @@
/**
* Advanced Ads.
*
* @author Thomas Maier <support@wpadvancedads.com>
* @license GPL-2.0+
* @link https://wpadvancedads.com
* @copyright 2013-2018 Thomas Maier, Advanced Ads GmbH
*/
;(function($){
/**
* Print Google AdSense account alerts after connecting to the API or after removing an alert.
*
* @param alerts
*/
function printAlerts( alerts ) {
var $div = $( '#mapi-account-alerts' );
$div.empty();
if ( alerts.length ) {
$div.append( $( '<h3 />' ).html( AdsenseMAPI.alertsHeadingMsg ) );
for ( var id in alerts.alerts ) {
var $alertBox = $( '<div class="card advads-notice-block advads-error"/>' );
var $dismissButton = $( ' <button type="button" class="mapi-dismiss-alert notice-dismiss" data-id="' + alerts.alerts[id]['id'] + '"><span class="screen-reader-text">' + AdsenseMAPI.alertsDismissMsg + '</span></button>' );
var msg = alerts.alerts[id].message;
if ( typeof AdsenseMAPI.alertsMsg[alerts.alerts[id]['id']] !== 'undefined' ) {
msg = AdsenseMAPI.alertsMsg[alerts.alerts[id]['id']];
}
$alertBox.append( $dismissButton );
$alertBox.append( msg );
$div.append( $alertBox );
}
}
}
$( document ).on( 'click', '.preventDefault', function( ev ) {
ev.preventDefault();
} );
$( document ).on( 'keypress', '#adsense input[type="text"]', function( ev ) {
if ( $( this ).hasClass( 'preventDefault' ) ) {
ev.preventDefault();
return;
}
if ( ev.which == 13 || ev.keyCode == 13 ) {
$( '#adsense .advads-settings-tab-main-form #submit' ).trigger( 'click' );
}
} );
$( document ).on( 'click', '#revoke-token', function(){
$( '#gadsense-freeze-all' ).css( 'display', 'block' );
var ID = $( '#adsense-id' ).val();
$.ajax({
url: ajaxurl,
type: 'post',
data: {
action: 'advads-mapi-revoke-token',
adsenseId: ID,
nonce: AdsenseMAPI.nonce,
},
success:function(response, status, XHR){
window.location.reload();
},
error:function(request, status, error){
$( '#gadsense-freeze-all' ).css( 'display', 'none' );
},
});
} );
$( document ).on( 'click', '#adsense-manual-config', function(){
$( '#adsense .form-table tr' ).css( 'display', 'table-row' );
$( '#adsense #auto-adsense-settings-div' ).css( 'display', 'none' );
$( '#adsense #full-adsense-settings-div' ).css( 'display', 'block' );
$( '#adsense-id' ).after( $( '#connect-adsense' ) );
$( '#adsense #submit' ).parent().show();
} );
// Open the code confirmation modal.
$( document ).on( 'click', '#connect-adsense', function(){
if ( $( this ).hasClass( 'disabled' ) ) return;
if ( 'undefined' != typeof window.advadsMapiConnect ) {
window.advadsMapiConnect( 'open-google' );
}
} );
$( document ).on( 'click', '.mapi-dismiss-alert', function( ev ) {
ev.preventDefault();
var pubId = $( '#adsense-id' ).val();
var alertId = $( this ).attr( 'data-id' );
$( '#gadsense-modal' ).css( 'display', 'block' );
$( '#gadsense-modal-outer' ).css( 'display', 'none' );
$.ajax({
url: ajaxurl,
type: 'post',
data: {
action: 'advads-mapi-dismiss-alert',
account: pubId,
id: alertId,
nonce: AdsenseMAPI.nonce,
},
success:function(response, status, XHR){
if ( 'undefined' != typeof response.alerts ) {
printAlerts( response );
}
$( '#gadsense-modal' ).css( 'display', 'none' );
$( '#gadsense-modal-outer' ).css( 'display', 'block' );
},
error:function(request, status, error){
$( '#gadsense-modal' ).css( 'display', 'none' );
$( '#gadsense-modal-outer' ).css( 'display', 'block' );
},
});
} );
$( document ).on( 'click', '.mapi-create-ads-txt', function( ev ) {
ev.preventDefault();
var top = jQuery( '#advads-ads-txt-wrapper' ).offset().top;
window.scrollTo( 0, top );
} );
$( document ).on( 'advadsMapiRefreshAlerts', function ( ev, response ) {
if ( 'undefined' != typeof response.status && response.status && response.alerts ) {
printAlerts( response );
}
} );
$( function(){
if ( $( '#adsense-id' ).val().trim() === '' ) {
$( '#adsense #submit' ).parent().css( 'display', 'none' );
}
} );
})(window.jQuery);

View File

@@ -0,0 +1,325 @@
<?php // phpcs:ignoreFile
/**
* View for the Settings - AdSense.
*
* @package Advanced_Ads
*/
use AdvancedAds\Utilities\Conditional;
$MAPI = Advanced_Ads_AdSense_MAPI::get_instance();
$options = $this->data->get_options();
$adsense_id = $this->data->get_adsense_id();
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
$mapi_account_details = false;
$CID = Advanced_Ads_AdSense_MAPI::CID;
$use_user_app = Advanced_Ads_AdSense_MAPI::use_user_app();
if ( $use_user_app ) {
$CID = ADVANCED_ADS_MAPI_CID;
}
$can_connect = true;
if ( $use_user_app && !( ( defined( 'ADVANCED_ADS_MAPI_CID' ) && '' != ADVANCED_ADS_MAPI_CID ) && ( defined( 'ADVANCED_ADS_MAPI_CIS' ) && '' != ADVANCED_ADS_MAPI_CIS ) ) ) {
$can_connect = false;
}
$has_token = Advanced_Ads_AdSense_MAPI::has_token( $adsense_id );
if ( $has_token && isset( $mapi_options['accounts'][ $adsense_id ]['details'] ) ) {
$mapi_account_details = $mapi_options['accounts'][ $adsense_id ]['details'];
}
$alerts = Advanced_Ads_AdSense_MAPI::get_stored_account_alerts( $adsense_id );
/* translators: 1: opening anchor tag for link to adsense account 2: closing anchor tag for link to adsense account */
$alerts_heading = $adsense_id ? sprintf( esc_html__( 'Warning from your %1$sAdSense account%2$s', 'advanced-ads' ), '<a target="_blank" href="https://www.google.com/adsense/new/u/1/' . esc_html( $adsense_id ) . '/">', '</a>' ) : esc_html__( 'AdSense warnings', 'advanced-ads' );
$alerts_heading = $adsense_id
? wp_kses(
sprintf(
/* translators: 1: opening anchor tag for link to adsense account 2: closing anchor tag for link to adsense account */
__( 'Warning from your %1$sAdSense account%2$s', 'advanced-ads' ),
'<a target="_blank" href="https://www.google.com/adsense/new/u/1/' . $adsense_id . '/">',
'</a>'
),
[
'a' => [
'target' => true,
'href' => true,
],
]
)
: __( 'AdSense warnings', 'advanced-ads' );
$alerts_dismiss = __( 'dismiss', 'advanced-ads' );
$connection_error_messages = Advanced_Ads_AdSense_MAPI::get_connect_error_messages();
$alerts_advads_messages = Advanced_Ads_Adsense_MAPI::get_adsense_alert_messages();
?>
<div id="mapi-account-alerts">
<?php if ( is_array( $alerts ) && isset( $alerts['items'] ) && is_array( $alerts['items'] ) && $alerts['items'] ) : ?>
<h3>
<?php
//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- already escaped
echo $alerts_heading;
?>
</h3>
<?php foreach ( $alerts['items'] as $alert_id => $alert ) : ?>
<div class="card advads-notice-block advads-error">
<button type="button" class="mapi-dismiss-alert notice-dismiss" data-id="<?php echo esc_attr( $alert_id ); ?>">
<span class="screen-reader-text"><?php echo esc_html( $alerts_dismiss ); ?></span>
</button>
<?php
$internal_id = $alert['id'] ?? str_replace( '-', '_', strtoupper( $alert['type'] ) );
echo wp_kses(
$alerts_advads_messages[ $internal_id ] ?? $alert['message'],
[
'a' => [
'href' => true,
'target' => true,
'class' => true,
],
]
);
?>
</div>
<?php endforeach; ?>
<?php /* translators: %s: date and time of last check in the format set in wp_options */ ?>
<p class="description alignright"><?php printf( __( 'last checked: %s', 'advanced-ads' ), $alerts['lastCheck'] ? esc_html( ( new DateTime( '@' . $alerts['lastCheck'], Advanced_Ads_Utils::get_wp_timezone() ) )->format( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ) : '-' ); ?></p>
<?php endif; ?>
<?php
if ( ! empty( $mapi_options['connect_error'] ) ) {
$message = isset( $mapi_options['connect_error']['message'] ) ? $mapi_options['connect_error']['message'] : '';
if ( isset( $connection_error_messages[ $mapi_options['connect_error']['reason'] ] ) ) {
$message = $connection_error_messages[ $mapi_options['connect_error']['reason'] ];
}
if ( ! empty( $message ) ) {
echo '<div id="mapi-connect-errors" class="notice error inline"><p class="advads-notice-inline advads-error">';
echo wp_kses( $message, [
'a' => [
'id' => [],
'class' => [],
'href' => [],
'style' => [],
],
'i' => [
'id' => [],
'class' => [],
'style' => [],
],
] );
echo '</p></div>';
}
}
?>
</div>
<div id="full-adsense-settings-div" <?php if ( empty( $adsense_id ) ) echo 'style="display:none"' ?>>
<input type="text" <?php echo $has_token ? 'readonly' : ''; ?> name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[adsense-id]" placeholder="pub-1234567891234567" style="margin-right:.8em" id="adsense-id" size="32" value="<?php echo esc_attr( $adsense_id ); ?>"/>
<?php if ( !empty( $adsense_id ) && !$has_token ) : ?>
<a id="connect-adsense" class="button-primary <?php echo ! Advanced_Ads_Checks::php_version_minimum() ? 'disabled ' : ''; ?>preventDefault" <?php if ( ! $can_connect || ! Advanced_Ads_Checks::php_version_minimum() ) echo 'disabled'; ?>><?php esc_attr_e( 'Connect to AdSense', 'advanced-ads' ) ?></a>
<?php endif; ?>
<?php if ( $has_token ) : ?>
<a id="revoke-token" class="button-secondary preventDefault"><?php esc_attr_e( 'Revoke API acccess', 'advanced-ads' ) ?></a>
<div id="gadsense-freeze-all" style="position:fixed;top:0;bottom:0;right:0;left:0;background-color:rgba(255,255,255,.5);text-align:center;display:none;">
<img alt="..." src="<?php echo ADVADS_BASE_URL . 'admin/assets/img/loader.gif'; ?>" style="margin-top:40vh" />
</div>
<?php endif; ?>
<?php if ( $mapi_account_details ) : ?>
<p class="description"><?php esc_html_e( 'Account holder name', 'advanced-ads' ); echo ': <strong>' . esc_html( $mapi_account_details['name'] ) . '</strong>'; ?></p>
<?php else : ?>
<?php if ( 0 !== strpos( $adsense_id, 'pub-' ) ) : ?>
<p class="advads-notice-inline advads-error"><?php esc_html_e( 'The Publisher ID has an incorrect format. (must start with "pub-")', 'advanced-ads' ); ?></p>
<?php endif; ?>
<?php endif; ?>
</div>
<?php if ( empty( $adsense_id ) ) : ?>
<div id="auto-adsense-settings-div" <?php if ( !empty( $adsense_id ) ) echo 'style="display:none;"' ?>>
<div class="widget-col">
<h3><?php _e( 'Yes, I have an AdSense account', 'advanced-ads' ) ?></h3>
<a id="connect-adsense" class="button-primary <?php echo ! Advanced_Ads_Checks::php_version_minimum() ? 'disabled ' : ''; ?>preventDefault" <?php echo ! Advanced_Ads_Checks::php_version_minimum() ? 'disabled' : ''; ?>><?php _e( 'Connect to AdSense', 'advanced-ads' ) ?></a>
<a id="adsense-manual-config" class="button-secondary preventDefault"><?php _e( 'Configure everything manually', 'advanced-ads' ) ?></a>
</div>
<div class="widget-col">
<h3><?php _e( "No, I still don't have an AdSense account", 'advanced-ads' ) ?></h3>
<a class="button button-secondary" target="_blank" href="<?php echo Advanced_Ads_AdSense_Admin::ADSENSE_NEW_ACCOUNT_LINK; ?>"><?php _e( 'Get a free AdSense account', 'advanced-ads' ); ?></a>
<p>
<?php
printf(
wp_kses(
/* translators: %1$s is an opening a tag, %2$s is the closing one */
__( 'See all %1$srecommended ad networks%2$s.', 'advanced-ads' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
'<a href="https://wpadvancedads.com/recommended-ad-networks/?utm_source=advanced-ads&utm_medium=link&utm_campaign=recommendations" target="_blank">',
'</a>'
);
?>
</p>
</div>
</div>
<style type="text/css">
#adsense table h3 {
margin-top: 0;
margin-bottom: .2em;
}
#adsense .form-table tr {
display: none;
}
#adsense .form-table tr:first-of-type {
display: table-row;
}
#auto-adsense-settings-div .widget-col {
float: left;
margin: 0px 5px 5px 0px;
}
#auto-adsense-settings-div:after {
display: block;
content: "";
clear: left;
}
#auto-adsense-settings-div .widget-col:first-child {
margin-right: 20px;
border-right: 1px solid #cccccc;
padding: 0px 20px 0px 0px;
position: relative;
}
#auto-adsense-settings-div .widget-col:first-child:after {
position: absolute;
content: "or";
display: block;
top: 20px;
right: -10px;
background: #ffffff;
color: #cccccc;
font-size: 20px;
}
@media screen and (max-width: 1199px) {
#auto-adsense-settings-div .widget-col { float: none; margin-right: 0; }
#auto-adsense-settings-div .widget-col:first-child { margin: 0px 0px 20px 0px; padding: 0px 0px 20px 0px; border-bottom: 1px solid #cccccc; border-right: 0; }
#auto-adsense-settings-div .widget-col:first-child:after { top: auto; right: auto; bottom: -10px; left: 20px; display: inline-block; padding: 0px 5px 0px 5px; }
}
</style>
<?php
echo "<br/><br/><br/><hr>";
include ADVADS_ABSPATH . 'modules/gadsense/admin/views/auto-ads-video.php';
?><p>
<a href="https://wpadvancedads.com/place-adsense-ad-unit-manually/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adsense-manually" style="text-decoration: none;" target="_blank"><span class="dashicons dashicons-welcome-learn-more"></span>
<?php
esc_attr_e( 'How to choose specific positions for AdSense ad units', 'advanced-ads' ); ?></a>
</p>
<?php
$_notice = 'nl_adsense';
if ( Advanced_Ads_Admin_Notices::get_instance()->can_display( $_notice ) && Conditional::user_can_subscribe( 'nl_first_steps' ) ) {
$box_classes = '!margin-top-4';
$text = sprintf(
/* translators: %s: number of add-ons. */
__( 'Subscribe to our free email course for Google AdSense, receive our newsletter for periodic tutorials, and get %s for Advanced Ads.', 'advanced-ads' ),
'<strong>' . __( '2 free add-ons', 'advanced-ads' ) . '</strong>'
);
include ADVADS_ABSPATH . '/admin/views/notices/inline.php';
}
?>
<?php else : ?>
<p>
<?php
printf(
wp_kses(
/* translators: %1$s is the opening link tag to our manual; %2$s is the appropriate closing link tag; %3$s is the opening link tag to our help forum; %4$s is the appropriate closing link tag */
__( 'Problems with AdSense? Check out the %1$smanual%2$s or %3$sask here%4$s.', 'advanced-ads' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
'<a href="https://wpadvancedads.com/adsense-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adsense-manual-check" target="_blank">',
'</a>',
'<a href="https://wordpress.org/support/plugin/advanced-ads/#new-post" target="_blank">',
'</a>'
); ?></p>
<p>
<?php
printf(
wp_kses(
/* translators: %1$s is an opening a tag, %2$s is the closing one */
__( 'See all %1$srecommended ad networks%2$s.', 'advanced-ads' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
'<a href="https://wpadvancedads.com/recommended-ad-networks/?utm_source=advanced-ads&utm_medium=link&utm_campaign=recommendations" target="_blank">',
'</a>'
);
?>
</p><?php endif; ?>
<?php if ( ! Advanced_Ads_Checks::php_version_minimum() ) : ?>
<p class="advads-notice-inline advads-error"><?php esc_html_e( 'Can not connect AdSense account. PHP version is too low.', 'advanced-ads' ); ?></p>
<?php endif; ?>
<div id="mapi-alerts-overlay">
<div style="position:relative;text-align:center;display:table;width:100%;height:100%;">
<div style="display:table-cell;vertical-align:middle;">
<img alt="loading" src="<?php echo esc_url( ADVADS_BASE_URL . 'admin/assets/img/loader.gif' ); ?>" />
</div>
</div>
</div>
<script type="text/javascript">
if ( 'undefined' == typeof window.AdsenseMAPI ) {
AdsenseMAPI = {};
}
AdsenseMAPI = Object.assign(
AdsenseMAPI,
<?php
echo wp_json_encode(
[
'alertsMsg' => $alerts_advads_messages,
'alertsHeadingMsg' => $alerts_heading,
'alertsDismissMsg' => wp_kses( $alerts_dismiss, [] ),
]
)
?>
);
</script>
<style type="text/css">
#adsense {
position: relative;
}
#mapi-alerts-overlay {
position:absolute;
top:0;
right:0;
bottom:0;
left:0;
background-color: rgb(255, 255, 255, .90);
display: none;
}
#mapi-account-alerts, #mapi-connect-errors {
margin-bottom: .5em;
}
#dissmiss-connect-error {
cursor: pointer;
}
#gadsense-overlay {
display:none;
background-color:rgba(255,255,255,.5);
position:absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
text-align:center;
}
</style>

View File

@@ -0,0 +1,197 @@
<?php // phpcs:ignoreFile
/**
* Renders the AdSense ad parameters metabox on the ad edit page.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*
* @var string $unit_type The type of the AdSense unit, e.g. 'responsive'.
* @var string $unit_resize The value of the resize option.
* @var string $unit_id The client ID.
* @var string $unit_code The slot ID.
* @var string $unit_pubid The publisher ID which differs if there comes a different it from the content.
* @var string $json_content The content as JSON.
* @var string $pub_id The publisher ID.
* @var Advanced_Ads_Ad_Type_Adsense $ad The AdSense Ad type.
* @var string $pub_id_errors A string with error messages.
* @var string $content The content.
* @var array $extra_params Filterable extra params that can be passed.
*/
if ( ! defined( 'WPINC' ) ) {
die();
}
$is_responsive = 'responsive' === $unit_type;
$is_link_responsive_unit = 'link-responsive' === $unit_type;
$is_matched_content = 'matched-content' === $unit_type;
$use_manual_css = 'manual' === $unit_resize;
if ( $is_responsive || $is_link_responsive_unit || $is_matched_content ) {
echo '<style> #advanced-ads-ad-parameters-size {display: none;} </style>';
}
$MAPI = Advanced_Ads_AdSense_MAPI::get_instance();
$use_user_app = Advanced_Ads_AdSense_MAPI::use_user_app();
$use_paste_code = true;
$use_paste_code = apply_filters( 'advanced-ads-gadsense-use-pastecode', $use_paste_code );
$db = Advanced_Ads_AdSense_Data::get_instance();
$adsense_id = trim( $db->get_adsense_id() );
$sizing_array = $db->get_responsive_sizing();
$gadsense_options = $db->get_options();
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
$mapi_nonce = wp_create_nonce( 'advads-mapi' );
$has_token = Advanced_Ads_AdSense_MAPI::has_token( $adsense_id );
$quota = $MAPI->get_quota();
$mapi_ad_codes = $mapi_options['ad_codes'];
$mapi_ad_codes['length'] = count( $mapi_ad_codes );
?>
<?php if ( $has_token ) : ?>
<script type="text/javascript">
if ( 'undefined' == typeof window.AdsenseMAPI ) {
var AdsenseMAPI = {};
}
AdsenseMAPI.hasToken = true;
AdsenseMAPI.nonce = '<?php echo $mapi_nonce ?>';
//AdsenseMAPI.codes = <?php echo json_encode( $mapi_ad_codes ) ?>;
AdsenseMAPI.quota = <?php echo json_encode( $quota ) ?>;
AdsenseMAPI.pubId = '<?php echo $pub_id ?>';
AdsenseMAPI.adStatus = '<?php echo $ad->get_status() ?>';
AdsenseMAPI.unsupportedUnits = <?php echo wp_json_encode( $mapi_options['unsupported_units'] ); ?>;
</script>
<?php endif; ?>
<script type="text/javascript">
if ( 'undefined' === typeof gadsenseData ) {
window.gadsenseData = {};
}
gadsenseData['pubId'] = '<?php echo $adsense_id; ?>';
gadsenseData['msg'] = {
unknownAd: '<?php esc_attr_e( "The ad details couldn't be retrieved from the ad code", 'advanced-ads' ); ?>',
pubIdMismatch: '<?php esc_attr_e( 'Warning: The AdSense account from this code does not match the one set in the Advanced Ads options.', 'advanced-ads' ); ?>'
};
</script>
<input type="hidden" id="advads-ad-content-adsense" name="advanced_ad[content]" value="<?php echo esc_attr( $json_content ); ?>"/>
<input type="hidden" name="unit_id" id="unit_id" value="<?php echo esc_attr( $unit_id ); ?>"/>
<?php if ( $use_paste_code ) : ?>
<div class="advads-adsense-code" style="display: none;">
<p class="description"><?php _e( 'Copy the ad code from your AdSense account, paste it into the area below and click on <em>Get details</em>.', 'advanced-ads' ); ?></p>
<textarea rows="10" cols="40" class="advads-adsense-content"></textarea>
<button class="button button-primary advads-adsense-submit-code"><?php _e( 'Get details', 'advanced-ads' ); ?></button>&nbsp;&nbsp;
<button class="button button-secondary advads-adsense-close-code"><?php _e( 'cancel', 'advanced-ads' ); ?></button>&nbsp;&nbsp;
<?php if ( ! $has_token ) : ?>
<a style="vertical-align:sub;font-weight:600;font-style:italic;" href="<?php echo admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' ) ?>"><?php _e( 'connect to your AdSense account', 'advanced-ads' ) ?></a>
<?php endif; ?>
<div id="pastecode-msg"></div>
</div>
<?php if ( $has_token && Advanced_Ads_Checks::php_version_minimum() ) {
Advanced_Ads_AdSense_Admin::get_mapi_ad_selector();
}
// the network variable needs to be set for the view to work!
$network = Advanced_Ads_Network_Adsense::get_instance();
include( ADVADS_ABSPATH . '/modules/gadsense/admin/views/external-ads-links.php' );
?>
<?php endif; ?>
<p id="adsense-ad-param-error"></p>
<?php ob_start(); ?>
<label class="label"><?php _e( 'Ad Slot ID', 'advanced-ads' ); ?></label>
<div>
<input type="text" name="unit-code" id="unit-code" value="<?php echo $unit_code; ?>"/>
<input type="hidden" name="advanced_ad[output][adsense-pub-id]" id="advads-adsense-pub-id" value="<?php echo esc_attr( $unit_pubid ); ?>"/>
<?php if ( $unit_pubid ) : ?>
<?php /* translators: %s is the publisher ID. */
printf( __( 'Publisher ID: %s', 'advanced-ads' ), $unit_pubid ); ?>
<?php endif; ?>
<p id="advads-pubid-in-slot" class="advads-notice-inline advads-error"
<?php echo ! ( 0 === strpos( $pub_id, 'pub-' ) && false !== strpos( $unit_code, substr( $pub_id, 4 ) ) ) ? 'style="display:none"' : ''; ?>
><?php _e( 'The ad slot ID is either a number or empty and not the same as the publisher ID.', 'advanced-ads' ) ?></p>
</div>
<hr/>
<?php
$unit_code_markup = ob_get_clean();
echo apply_filters( 'advanced-ads-gadsense-unit-code-markup', $unit_code_markup, $unit_code );
if ( $pub_id_errors ) : ?>
<p>
<span class="advads-notice-inline advads-error">
<?php echo $pub_id_errors; ?>
</span>
<?php /* translators: %s the setting page link */
printf( __( 'Please <a href="%s" target="_blank">change it here</a>.', 'advanced-ads' ), admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' ) ); ?>
</p>
<?php endif; ?>
<label class="label" id="unit-type-block"><?php _e( 'Type', 'advanced-ads' ); ?></label>
<div>
<select name="unit-type" id="unit-type">
<option value="normal" <?php selected( $unit_type, 'normal' ); ?>><?php _e( 'Fixed Size', 'advanced-ads' ); ?></option>
<option value="responsive" <?php selected( $unit_type, 'responsive' ); ?>><?php _e( 'Responsive', 'advanced-ads' ); ?></option>
<option value="matched-content" <?php selected( $unit_type, 'matched-content' ); ?>><?php esc_html_e( 'Multiplex', 'advanced-ads' ); ?></option>
<?php if ( $unit_type === 'link' ) : ?>
<option value="link" <?php selected( $unit_type, 'link' ); ?>><?php _e( 'Link ads', 'advanced-ads' ); ?></option>
<?php endif; ?>
<?php if ( $unit_type === 'link-responsive' ) : ?>
<option value="link-responsive" <?php selected( $unit_type, 'link-responsive' ); ?>><?php _e( 'Link ads (Responsive)', 'advanced-ads' ); ?></option>
<?php endif; ?>
<option value="in-article" <?php selected( $unit_type, 'in-article' ); ?>><?php _e( 'In-article', 'advanced-ads' ); ?></option>
<option value="in-feed" <?php selected( $unit_type, 'in-feed' ); ?>><?php _e( 'In-feed', 'advanced-ads' ); ?></option>
</select>
<a href="https://wpadvancedads.com/google-adsense-ad-formats/?utm_source=advanced-ads&utm_medium=link&utm_campaign=adsense-ad-types" class="advads-manual-link" target="_blank"><?php esc_html_e( 'Manual', 'advanced-ads' ); ?></a>
</div>
<?php if ( in_array( $unit_type, [ 'link', 'link-responsive' ], true ) ) : ?>
<p class="advads-message-warning"><?php esc_html_e( 'Google AdSense deprecated Link Units. Please choose another type.', 'advanced-ads' ); ?>
<a href="https://wpadvancedads.com/adsense-link-units/" target="_blank" rel="noopener">
<?php esc_html_e( 'Learn more', 'advanced-ads' ); ?>
</a>
</p>
<?php endif; ?>
<hr/>
<label class="label" <?php if ( ! $is_responsive || 2 > count( $sizing_array ) ) {
echo 'style="display: none;"';
} ?> id="resize-label"><?php _e( 'Resizing', 'advanced-ads' ); ?></label>
<div <?php if ( ! $is_responsive || 2 > count( $sizing_array ) ) {
echo 'style="display: none;"';
} ?>>
<select name="ad-resize-type" id="ad-resize-type">
<?php foreach ( $sizing_array as $key => $desc ) : ?>
<option value="<?php echo $key; ?>" <?php selected( $key, $unit_resize ); ?>><?php echo $desc; ?></option>
<?php endforeach; ?>
</select>
</div>
<hr>
<label class="label advads-adsense-layout" <?php if ( 'in-feed' !== $unit_type ) {
echo 'style="display: none;"';
} ?> id="advads-adsense-layout"><?php _e( 'Layout', 'advanced-ads' ); ?></label>
<div <?php if ( 'in-feed' !== $unit_type ) {
echo 'style="display: none;"';
} ?>>
<input name="ad-layout" id="ad-layout" value="<?php echo isset( $layout ) ? $layout : ''; ?>"/>
</div>
<hr>
<label class="label advads-adsense-layout-key" <?php if ( 'in-feed' !== $unit_type ) {
echo 'style="display: none;"';
} ?> id="advads-adsense-layout-key"><?php _e( 'Layout-Key', 'advanced-ads' ); ?></label>
<div <?php if ( 'in-feed' !== $unit_type ) {
echo 'style="display: none;"';
} ?>>
<input type="text" name="ad-layout-key" id="ad-layout-key" value="<?php echo $layout_key ?? ''; ?>"/>
</div>
<hr/>
<label class="label clearfix-before" <?php if ( ! $is_responsive ) {
echo 'style="display: none;"';
} ?>><?php _e( 'Clearfix', 'advanced-ads' ); ?></label>
<div class="clearfix-before" <?php if ( ! $is_responsive ) {
echo 'style="display: none;"';
} ?>>
<input type="checkbox" name="advanced_ad[output][clearfix_before]" value="1" <?php checked( ! empty( $options['output']['clearfix_before'] ), true ); ?> />
<p class="description">
<?php _e( 'Enable this if responsive ads cover something on your site.', 'advanced-ads' ); ?>
</p>
</div>
<hr class="clearfix-before" <?php if ( ! $is_responsive ) {
echo 'style="display: none;"';
} ?> />
<?php do_action( 'advanced-ads-gadsense-extra-ad-param', $extra_params, $content, $ad );

View File

@@ -0,0 +1,37 @@
<?php
/**
* AdSense report markup.
*
* @var Advanced_Ads_AdSense_Report $this report instance.
* @var array $report_domains domain names included in the report.
* @var string $report_filter ad unit or domain name to filter the output with.
* @var array $sums daily sums of earnings.
* @var string $earning_cells markup for each earning period.
*/
$time_zone = Advanced_Ads_Utils::get_wp_timezone();
$data_timestamp = date_create( '@' . $this->get_data()->get_timestamp() );
$data_timestamp->setTimezone( $time_zone );
?>
<div class="advads-flex">
<?php echo wp_kses_post( $earning_cells ); ?>
<div class="advads-flex1 advads-stats-box">
<?php if ( $this->type === 'domain' ) : // Adds the dropdown list of domain names. ?>
<div class="advads-stats-dd-container">
<div class="advads-stats-dd-button"><span class="dashicons dashicons-admin-multisite"></span>
<div class="advads-stats-dd-items">
<div class="advads-stats-dd-item<?php echo in_array( $report_filter, [ '*', '' ], true ) ? ' current-filter' : ''; ?>" data-domain="*">
<?php esc_html_e( 'All', 'advanced-ads' ); ?>
</div><!-- .advads-stats-dd-item -->
<?php foreach ( $report_domains as $domain_name ) : ?>
<div class="advads-stats-dd-item<?php echo ( $domain_name === $report_filter ) ? ' current-filter' : ''; ?>" data-domain="<?php echo esc_attr( $domain_name ); ?>">
<?php echo esc_html( $domain_name ); ?>
</div><!-- .advads-stats-dd-item -->
<?php endforeach; ?>
</div><!-- .advads-stats-dd-items -->
</div><!-- .advads-stats-dd-button -->
</div><!-- .advads-stats-dd-container -->
<?php endif; ?>
<div class="advads-stats-age"><?php echo esc_html( $data_timestamp->format( get_option( 'time_format' ) ) ); ?></div>
</div><!-- .advads-stats-box-->
</div><!-- .advads-flex -->

View File

@@ -0,0 +1,16 @@
<div id="advads-auto-ads-links">
<a id="advads-auto-ads-video-link" style="cursor: pointer;">
<span class="dashicons dashicons-format-video"></span>&nbsp;<?php
esc_attr_e( 'How to enable Auto ads in 30 seconds (video tutorial)', 'advanced-ads' ); ?>
</a>
</div>
<script>
(function ($) {
var $videoLink = $('#advads-auto-ads-video-link');
$videoLink.click(function () {
$('<br class="clear"/><br/><iframe src="https://player.vimeo.com/video/381874350" width="640" height="360" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>').appendTo('#advads-auto-ads-links');
$(this).remove();
})
.children('.dashicons').css('line-height', $videoLink.css('line-height'));
})(jQuery)
</script>

View File

@@ -0,0 +1,11 @@
<?php printf(
wp_kses(
/* translators: 1: the plugin name that is managing the Auto ads code. */
__( 'Advanced Ads detected that <strong>%s</strong> is managing the Auto ads code and will therefore not add it.', 'advanced-ads' ),
[
'strong' => [],
]
),
'Borlabs Cookies'
);

View File

@@ -0,0 +1,148 @@
<?php
/**
* HTML markup for AdSense connection modal frame.
*
* @package AdvancedAds
*/
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Conditional;
$data_obj = Advanced_Ads_AdSense_Data::get_instance();
$options = $data_obj->get_options();
$nonce = wp_create_nonce( 'advads-mapi' );
$cid = Advanced_Ads_AdSense_MAPI::CID;
$use_user_app = Advanced_Ads_AdSense_MAPI::use_user_app();
if ( $use_user_app ) {
$cid = ADVANCED_ADS_MAPI_CID;
}
$state = [
'api' => 'adsense',
'nonce' => $nonce,
'return_url' => admin_url( 'admin.php?page=advanced-ads-settings&oauth=1#top#adsense' ),
];
$connection_error_messages = Advanced_Ads_AdSense_MAPI::get_connect_error_messages();
$auth_url = 'https://accounts.google.com/o/oauth2/v2/auth?scope=' .
rawurlencode( 'https://www.googleapis.com/auth/adsense.readonly' ) .
'&client_id=' . $cid .
'&redirect_uri=' . rawurlencode( Advanced_Ads_AdSense_MAPI::REDIRECT_URI ) .
'&state=' . rawurlencode( base64_encode( wp_json_encode( $state ) ) ) . // phpcs:ignore
'&access_type=offline&include_granted_scopes=true&prompt=consent&response_type=code';
$_get = wp_unslash( $_GET );
if ( '1' === Params::get( 'oauth' ) && 'adsense' === Params::get( 'api' ) ) : ?>
<?php if ( isset( $_get['nonce'] ) && false !== wp_verify_nonce( $_get['nonce'], 'advads-mapi' ) ) : ?>
<?php if ( isset( $_get['code'] ) && Conditional::user_can( 'advanced_ads_manage_options' ) ) : ?>
<input type="hidden" id="advads-adsense-oauth-code" value="<?php echo esc_attr( urldecode( $_get['code'] ) ); ?>" />
<?php endif; ?>
<?php endif; ?>
<?php endif; ?>
<div id="gadsense-modal" data-return="<?php echo ESC_ATTR( admin_url( 'admin.php?page=advanced-ads-settings&oauth=1#top#adsense' ) ); ?>">
<div id="gadsense-modal-outer">
<div id="gadsense-modal-inner">
<div id="gadsense-modal-content">
<div class="gadsense-modal-content-inner" data-content="confirm-code">
<i class="dashicons dashicons-dismiss"></i>
<h2><?php esc_html_e( 'Processing authorization', 'advanced-ads' ); ?></h2>
<div class="gadsense-overlay">
<img alt="..." src="<?php echo esc_url( ADVADS_BASE_URL ); ?>admin/assets/img/loader.gif" style="margin-top:3em" />
</div>
</div>
<div class="gadsense-modal-content-inner" data-content="error" style="display:none;">
<i class="dashicons dashicons-dismiss"></i>
<h3><?php esc_html_e( 'Cannot access your account information.', 'advanced-ads' ); ?></h3>
<p class="error-message advads-notice-inline advads-error"></p>
<p class="error-description" style="font-size:1.1em;"></p>
</div>
<div class="gadsense-modal-content-inner" data-content="account-selector" style="display:none;">
<i class="dashicons dashicons-dismiss"></i>
<h3><?php esc_html_e( 'Please select an account', 'advanced-ads' ); ?></h3>
<p>
<select id="mapi-select-account">
</select>
</p>
<p><button class="button-primary"><?php esc_html_e( 'Use this account', 'advanced-ads' ); ?></button></p>
<input type="hidden" class="token-data" value="" />
<input type="hidden" class="accounts-details" value="" />
<div class="gadsense-overlay">
<img alt="..." src="<?php echo esc_url( ADVADS_BASE_URL ); ?>admin/assets/img/loader.gif" style="margin-top:3em" />
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
if ( 'undefined' == typeof window.AdsenseMAPI ) {
AdsenseMAPI = {};
}
AdsenseMAPI.nonce = '<?php echo wp_strip_all_tags( $nonce ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>';
AdsenseMAPI.oAuth2 = '<?php echo wp_strip_all_tags( $auth_url ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>';
AdsenseMAPI.connectErrorMsg = <?php echo wp_json_encode( $connection_error_messages ); ?>;
</script>
<style type="text/css">
.gadsense-overlay {
display:none;
background-color:rgba(255,255,255,.5);
position:absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
text-align:center;
}
#gadsense-modal {
display: none;
background-color: rgba(0,0,0,.5);
position:fixed;
top:0;
left:0;
right:0;
bottom:0;
}
#gadsense-modal-outer {
position: relative;
width: 60%;
height: 100%;
<?php if ( is_rtl() ) : ?>
margin-right: 20%;
<?php else : ?>
margin-left: 20%;
<?php endif; ?>
}
#gadsense-modal-inner {
display: table;
width: 100%;
height: 100%;
}
#gadsense-modal-content {
display: table-cell;
vertical-align: middle;
}
.gadsense-modal-content-inner {
padding: 1em;
background-color: #f0f0f0;
position: relative;
border: 3px solid #808b94;
}
.gadsense-modal-content-inner .dashicons-dismiss {
background-color: #fff;
border-radius: 100%;
cursor: pointer;
top: -.5em;
<?php if ( is_rtl() ) : ?>
left: -.5em;
<?php else : ?>
right: -.5em;
<?php endif; ?>
position: absolute;
z-index: 2;
}
</style>

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,60 @@
<?php
/**
* WARNING: be careful when modifying the DOM of this document!
* there are some jquery calls that rely on this structure!
*
* @package AdvancedAds
*/
$is_account_connected = $network->is_account_connected();
?>
<p>
<span class="mapi-insert-code">
<a href="#">
<?php
printf(
/* translators: 1: The name of an ad network. */
esc_html__( 'Insert new %1$s code', 'advanced-ads' ),
esc_html( $network->get_display_name() )
);
?>
</a>
</span>
<?php if ( Advanced_Ads_Checks::php_version_minimum() ) : ?>
<?php if ( $is_account_connected ) : ?>
<span class="mapi-open-selector">
<span class="mapi-optional-or"><?php esc_html_e( 'or', 'advanced-ads' ); ?></span>
<a href="#" class="prevent-default"><?php esc_html_e( 'Get ad code from your linked account', 'advanced-ads' ); ?></a>
</span>
<?php if ( $network->supports_manual_ad_setup() ) : ?>
<span class="mapi-close-selector-link">
<?php esc_html_e( 'or', 'advanced-ads' ); ?><a href="#" class="prevent-default">
<?php
printf(
/* translators: 1: The name of an ad network. */
esc_html__( 'Set up %1$s code manually', 'advanced-ads' ),
esc_html( $network->get_display_name() )
);
?>
</a>
</span>
<?php endif; ?>
<?php else : ?>
<?php
esc_html_e( 'or', 'advanced-ads' );
$connect_link_label = sprintf(
/* translators: 1: The name of an ad network. */
esc_html__( 'Connect to %1$s', 'advanced-ads' ),
esc_html( $network->get_display_name() )
);
?>
<a href="<?php echo esc_url( $network->get_settings_href() ); ?>" style="padding:0 10px;font-weight:bold;"><?php echo esc_html( $connect_link_label ); ?></a>
<?php endif; ?>
<?php endif; ?>
</p>
<?php if ( $is_account_connected && ! Advanced_Ads_Checks::php_version_minimum() ) : ?>
<p class="advads-notice-inline advads-error"><?php esc_html_e( 'Can not connect AdSense account. PHP version is too low.', 'advanced-ads' ); ?></p>
<?php
endif;

View File

@@ -0,0 +1,138 @@
<?php
/**
* AdSense ad units table.
*
* @var bool $closeable
* @var bool $use_dashicons
* @var Advanced_Ads_Network_Adsense $network
* @var array $ad_units
* @var bool $display_slot_id
* @var string $pub_id
*
* @package AdvancedAds
*/
global $external_ad_unit_id, $closeable, $display_slot_id;
if ( ! isset( $hide_idle_ads ) ) {
$hide_idle_ads = true;
}
if ( ! isset( $ad_units ) ) {
$ad_units = [];
}
?>
<div id="mapi-wrap" class="aa-select-list">
<?php if ( $closeable ) : ?>
<button type="button" id="mapi-close-selector" class="notice-dismiss"></button>
<?php endif; ?>
<i id="mapi-archived-ads" title="<?php esc_attr_e( 'Hide archived ads', 'advanced-ads' ); ?>" data-alt-title="<?php esc_attr_e( 'Show archived ads', 'advanced-ads' ); ?>" class="dashicons dashicons-hidden"></i>
<i class="aa-select-list-update dashicons dashicons-update mapiaction" data-mapiaction="updateList" style="color:#0085ba;cursor:pointer;font-size:20px;" title="<?php esc_attr_e( 'Update the ad units list', 'advanced-ads' ); ?>"></i>
<div id="mapi-loading-overlay" class="aa-select-list-loading-overlay">
<img alt="..." src="<?php echo esc_url( ADVADS_BASE_URL . 'admin/assets/img/loader.gif' ); ?>" style="margin-top:8em;" />
</div>
<div id="mapi-table-wrap" class="aa-select-list-table-wrap">
<table class="widefat striped">
<thead>
<tr>
<th><?php esc_html_e( 'Name', 'advanced-ads' ); ?></th>
<?php if ( $display_slot_id ) : ?>
<th><?php echo esc_html_x( 'Slot ID', 'AdSense ad', 'advanced-ads' ); ?></th>
<?php endif; ?>
<th><?php echo esc_html_x( 'Type', 'AdSense ad', 'advanced-ads' ); ?></th>
<th><?php esc_html_e( 'Size', 'advanced-ads' ); ?></th>
</tr>
</thead>
<tbody>
<?php if ( empty( $ad_units ) ) : ?>
<tr id="mapi-notice-noads">
<td colspan="5" style="text-align:center;">
<?php esc_attr_e( 'No ad units found', 'advanced-ads' ); ?>
<button type="button" class="mapiaction icon-button" data-mapiaction="updateList">
<?php esc_attr_e( 'Update the ad units list', 'advanced-ads' ); ?>
<i class="dashicons dashicons-update" style="color:#0085ba;font-size:20px;"></i>
</button>
</td>
</tr>
<?php
else :
// Force a refresh the first time the ad list is opened after an update.
echo ! isset( $ad_units[0]->raw['nameV2'] ) ? '<input type="hidden" id="mapi-force-v2-list-update" value="" />' : '';
foreach ( $ad_units as $ad_unit ) {
$ad_unit->is_supported = $network->is_supported( $ad_unit );
}
$sorted_adunits = Advanced_Ads_Ad_Network_Ad_Unit::sort_ad_units( $ad_units, $external_ad_unit_id );
?>
<?php foreach ( $sorted_adunits as $unit ) : ?>
<tr <?php echo 'ARCHIVED' === $unit->raw['status'] ? 'data-archived="1"' : ''; ?> class="advads-clickable-row mapiaction" data-mapiaction="getCode" data-slotid="<?php echo esc_attr( $unit->id ); ?>" data-active="<?php echo esc_attr( $unit->active ); ?>">
<td><?php echo esc_html( $unit->name ); ?></td>
<?php if ( $display_slot_id ) : ?>
<td class="unitcode">
<?php
echo '<span>' . esc_html( $unit->slot_id ) . '</span>';
echo 'ARCHIVED' === $unit->raw['status'] ? '&nbsp;<code>' . esc_html__( 'Archived', 'advanced-ads' ) . '</code>' : '';
?>
</td>
<?php endif; ?>
<td class="unittype">
<?php if ( $unit->is_supported ) : ?>
<?php if ( ! empty( $unit->code ) ) : ?>
<?php echo esc_attr( Advanced_Ads_AdSense_MAPI::format_ad_data( $unit, 'type' ) ); ?>
<?php else : ?>
<button type="button" class="button-secondary button-small" title="<?php esc_attr_e( 'Get the code for this ad', 'advanced-ads' ); ?>">
<span style="line-height: 26px;" class="dashicons dashicons-update"></span> <?php esc_html_e( 'Load', 'advanced-ads' ); ?>
</button>
<?php endif; ?>
<?php elseif ( empty( $unit->code ) ) : ?>
<span class="dashicons dashicons-warning" title="<?php esc_attr_e( 'Ad can\'t be imported, click for details', 'advanced-ads' ); ?>"></span>
<?php endif; ?>
</td>
<td class="unitsize">
<?php echo esc_attr( Advanced_Ads_AdSense_MAPI::format_ad_data( $unit, 'size' ) ); ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<p class="advads-notice-inline advads-error" id="remote-ad-code-error" style="display:none;"><strong><?php esc_attr_e( 'Unrecognized ad code', 'advanced-ads' ); ?></strong></p>
<p class="advads-error-message" id="remote-ad-code-msg"></p>
</div>
<div style="display:none;" id="remote-ad-unsupported-ad-type">
<h3 class="advads-notice-inline advads-error">
<?php esc_html_e( 'This ad type can currently not be imported from AdSense.', 'advanced-ads' ); ?>
</h3>
<p>
<?php esc_html_e( 'You can proceed with one of the following solutions', 'advanced-ads' ); ?>:
</p>
<ul>
<li>
<?php
/* Translators: 1: opening tag for AdSense account link 2: opening tag for a link to insert ad code 3: closing a tag */
printf( esc_html__( '%1$sCopy the code from your AdSense account%3$s and %2$sinsert a new AdSense code here%3$s.', 'advanced-ads' ), '<a href="https://www.google.com/adsense/new/u/0/' . esc_attr( $pub_id ) . '/myads/units" target="_blank">', '<a href="#" class="mapi-insert-code prevent-default">', '</a>' );
?>
</li>
<li>
<?php
/* Translators: 1: opening tag for a link to create an ad manually 2: closing a tag */
printf(
wp_kses(
/* translators: 1: opening tag for a link to create an ad manually 2: closing tag */
__( '%1$sCreate an AdSense code manually%2$s: Select the <em>Normal</em> or <em>Responsive</em> type and the size.', 'advanced-ads' ),
[
'em' => [],
'strong' => [],
]
),
'<a href="#" class="mapi-close-selector-link prevent-default">',
'</a>'
);
?>
</li>
<li>
<?php esc_html_e( 'Choose a different ad from your AdSense account above.', 'advanced-ads' ); ?>
</li>
</ul>
</div>

View File

@@ -0,0 +1,15 @@
<?php
/**
* Option to enable AdSense Auto ads on AMP pages
* located under Advanced Ads > Settings > AdSense > Auto ads
*
* @var string $option_name name of the option.
* @var bool $auto_ads_enabled true if the AMP Auto ads option is enabled.
*/
?>
<p>
<label>
<input type="checkbox" name="<?php echo esc_attr( $option_name ); ?>[auto_ads_enabled]" value="1" <?php checked( $auto_ads_enabled ); ?>/>
<?php esc_html_e( 'Enable AMP Auto ads', 'advanced-ads' ); ?>
</label>
</p>

View File

@@ -0,0 +1,19 @@
<?php // phpcs:ignoreFile
$path = dirname( __FILE__ );
return [
'classmap' => [
'Advanced_Ads_Ad_Type_Adsense' => $path . '/includes/class-ad-type-adsense.php',
'Advanced_Ads_AdSense_Data' => $path . '/includes/class-gadsense-data.php',
'Advanced_Ads_AdSense_MAPI' => $path . '/includes/class-mapi.php',
'Advanced_Ads_AdSense_Admin' => $path . '/admin/admin.php',
'Advanced_Ads_AdSense_Public' => $path . '/public/public.php',
'Advanced_Ads_AdSense_Report' => $path . '/includes/class-adsense-report.php',
'AdSense_Report_Data' => $path . '/includes/class-adsense-report-data.php',
'Advanced_Ads_AdSense_Report_Api' => $path . '/includes/adsense-report-api.php',
'Advanced_Ads_Network_Adsense' => $path . '/includes/class-network-adsense.php',
'AdvancedAds\\Adsense\\Types\\Adsense' => $path . '/includes/types/type-adsense.php',
],
'textdomain' => null,
];

View File

@@ -0,0 +1,172 @@
<?php // phpcs:ignore WordPress.Files.FileName
/**
* Report API for AdSense.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
/**
* Retrieve report data from Google.
*/
class Advanced_Ads_AdSense_Report_Api {
/**
* Version of the AdSense Management API in use (for getting fresh data).
*
* @var string
*/
const API_VERSION = '2.0';
/**
* Report API endpoint.
*
* @var string
*/
private $endpoint_url;
/**
* Report type
*
* @var string
*/
private $type;
/**
* The API access token or an error array.
*
* @var array|string
*/
private $access_token;
/**
* The current connected AdSense account.
*
* @var string
*/
private $publisher_id;
/**
* Instance constructor.
*
* @param string $type report type.
*/
public function __construct( $type ) {
$publisher_id = Advanced_Ads_AdSense_Data::get_instance()->get_adsense_id();
$this->type = $type;
$this->access_token = Advanced_Ads_AdSense_MAPI::get_access_token( $publisher_id );
$this->publisher_id = $publisher_id;
$endpoint_args = [
'startDate.year' => '%SY%', // Start date's year - integer (4 digits).
'startDate.month' => '%SM%', // Start date's month - integer.
'startDate.day' => '%SD%', // Start date's integer - integer.
'endDate.year' => '%EY%', // End date's year - integer (4 digits).
'endDate.month' => '%EM%', // End date's month - integer.
'endDate.day' => '%ED%', // End date's integer - integer.
'dimension1' => '%DIM%', // Primary reporting dimension (domain name or ad unit name).
'dimension2' => 'DATE', // Secondary reporting dimension.
'metrics' => 'ESTIMATED_EARNINGS', // Report metrics.
'reportingTimeZone' => 'ACCOUNT_TIME_ZONE', // Time zone used in report data.
];
$this->endpoint_url = str_replace( [ 'dimension1', 'dimension2' ], 'dimensions', add_query_arg( $endpoint_args, 'https://adsense.googleapis.com/v2/accounts/%pubid%/reports:generate' ) );
}
/**
* Checks if the current setup has an access token.
*
* @return bool true if there is a token.
*/
public function has_token() {
return is_string( $this->access_token );
}
/**
* Get access token related error message.
*
* @return array Array of error messages.
*/
public function get_token_error() {
return is_string( $this->access_token ) ? [] : $this->access_token;
}
/**
* Check if there is an error related to access tokens.
*
* @return bool true if any error happened when requesting an access token.
*/
public function has_token_error() {
return ! is_string( $this->access_token );
}
/**
* Perform the actual call to Google for fresh data.
*
* @return array associative array with the response or with error data in case of failure.
*/
public function call_google() {
$dimension = 'unit' === $this->type ? 'AD_UNIT_ID' : 'DOMAIN_NAME';
$today = new DateTimeImmutable();
$start_date = $today->sub( date_interval_create_from_date_string( '28 days' ) );
// Replace placeholder in the endpoint with actual arguments.
$url = str_replace(
[
'%pubid%',
'%DIM%',
'%SY%',
'%SM%',
'%SD%',
'%EY%',
'%EM%',
'%ED%',
],
[
$this->publisher_id,
$dimension,
$start_date->format( 'Y' ),
$start_date->format( 'n' ),
$start_date->format( 'j' ),
$today->format( 'Y' ),
$today->format( 'n' ),
$today->format( 'j' ),
],
$this->endpoint_url
);
$headers = [
'Authorization' => 'Bearer ' . $this->access_token,
];
$response = wp_remote_get( $url, [ 'headers' => $headers ] );
Advanced_Ads_AdSense_MAPI::log( 'Fetched AdSense Report from ' . $url );
if ( is_wp_error( $response ) ) {
return [
'status' => false,
/* translators: AdSense ID. */
'msg' => sprintf( esc_html__( 'Error while retrieving report for "%s".', 'advanced-ads' ), $this->publisher_id ),
'raw' => $response->get_error_message(),
];
}
$response_body = json_decode( $response['body'], true );
if ( ! isset( $response_body['startDate'] ) ) {
return [
'status' => false,
/* translators: AdSense ID. */
'msg' => sprintf( esc_html__( 'Invalid response while retrieving report for "%s".', 'advanced-ads' ), $this->publisher_id ),
'raw' => $response['body'],
];
}
$response_body['api_version'] = self::API_VERSION;
$response_body['timestamp'] = time();
return [
'status' => true,
'response_body' => $response_body,
];
}
}

View File

@@ -0,0 +1,397 @@
<?php // phpcs:ignore WordPress.Files.FileName
/**
* AdSense Ad Type
*
* @package Advanced_Ads
* @author Thomas Maier <support@wpadvancedads.com>
* @license GPL-2.0+
* @link https://wpadvancedads.com
* @copyright 2013-2022 Thomas Maier, Advanced Ads GmbH
*/
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Interfaces\Ad_Interface;
/**
* Adsense ad type
*
* phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
*/
class Advanced_Ads_Ad_Type_Adsense extends Ad implements Ad_Interface {
/**
* Return an array with AdSense ad type keys and readable labels
*
* @return array
*/
public static function get_ad_types() {
return [
'normal' => __( 'Normal', 'advanced-ads' ),
'responsive' => __( 'Responsive', 'advanced-ads' ),
'matched-content' => __( 'Multiplex', 'advanced-ads' ),
'link' => __( 'Link ads', 'advanced-ads' ),
'link-responsive' => __( 'Link ads (Responsive)', 'advanced-ads' ),
'in-article' => __( 'In-article', 'advanced-ads' ),
'in-feed' => __( 'In-feed', 'advanced-ads' ),
];
}
/**
* Get readable names for each AdSense ad type
*
* @param string $ad_type ad type key.
* @return string
*/
public static function get_ad_type_label( $ad_type ) {
$ad_types = self::get_ad_types();
return $ad_types[ $ad_type ] ?? __( 'Normal', 'advanced-ads' );
}
/**
* Output for the ad parameters metabox
* this will be loaded using ajax when changing the ad type radio buttons
* echo the output right away here
* name parameters must be in the "advanced_ads" array
*
* @param object $ad ad object.
*
* @since 1.4
*/
public function render_parameters( $ad ) {
// TODO: THIS IS JUST A QUICK AND DIRTY HACK. Create a dedicated method to handle this properly.
?>
<script>
jQuery( function () {
<?php
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
$json_ad_codes = wp_json_encode( $mapi_options['ad_codes'] );
?>
const adsense = new AdvancedAdsNetworkAdsense(<?php echo $json_ad_codes; // phpcs:ignore ?>)
AdvancedAdsAdmin.AdImporter.setup( adsense )
} )
</script>
<?php
$content = (string) $ad->get_content();
$unit_id = '';
$unit_pubid = '';
$unit_code = '';
$unit_type = 'responsive';
$unit_width = 0;
$unit_height = 0;
$json_content = '';
$unit_resize = '';
$extra_params = [
'default_width' => '',
'default_height' => '',
'at_media' => [],
];
$db = Advanced_Ads_AdSense_Data::get_instance();
$pub_id = trim( $db->get_adsense_id( $ad ) );
// check pub_id for errors.
$pub_id_errors = false;
if ( '' !== $pub_id && 0 !== strpos( $pub_id, 'pub-' ) ) {
$pub_id_errors = __( 'The Publisher ID has an incorrect format. (must start with "pub-")', 'advanced-ads' );
}
global $external_ad_unit_id, $use_dashicons, $closeable;
$closeable = true;
$use_dashicons = false;
$external_ad_unit_id = '';
if ( trim( $content ) !== '' ) {
$json_content = stripslashes( $content );
// get json content striped by slashes.
$content = json_decode( stripslashes( $content ) );
if ( isset( $content->unitType ) ) {
$content->json = $json_content;
$unit_type = $content->unitType;
$unit_code = $content->slotId;
$unit_pubid = ! empty( $content->pubId ) ? $content->pubId : $pub_id;
$layout = $content->layout ?? '';
$layout_key = $content->layout_key ?? '';
if ( 'responsive' !== $content->unitType && 'link-responsive' !== $content->unitType && 'matched-content' !== $content->unitType ) {
// Normal ad unit.
$unit_width = $ad->get_width();
$unit_height = $ad->get_height();
} else {
// Responsive && multiplex ads.
$unit_resize = $content->resize ?? 'auto';
if ( 'auto' !== $unit_resize ) {
$extra_params = apply_filters( 'advanced-ads-gadsense-ad-param-data', $extra_params, $content, $ad );
}
}
if ( ! empty( $unit_pubid ) ) {
$unit_id = 'ca-' . $unit_pubid . ':' . $unit_code;
}
$external_ad_unit_id = $unit_id;
}
}
if ( '' === trim( $pub_id ) && '' !== trim( $unit_code ) ) {
$pub_id_errors = __( 'Your AdSense Publisher ID is missing.', 'advanced-ads' );
}
$default_template = GADSENSE_BASE_PATH . 'admin/views/adsense-ad-parameters.php';
/**
* Inclusion of other UI template is done here. The content is passed in order to allow the inclusion of different
* templates file, depending of the ad. It's up to the developer to verify that $content is not an empty
* variable (which is the case for a new ad).
*
* Inclusion of .js and .css files for the ad creation/editon page are done by another hook. See
* 'advanced-ads-gadsense-ad-param-script' and 'advanced-ads-gadsense-ad-param-style' in "../admin/class-gadsense-admin.php".
*/
$template = apply_filters( 'advanced-ads-gadsense-ad-param-template', $default_template, $content );
require $template;
}
/**
* Render icon on the ad overview list
*
* @param Ad $ad Ad instance.
*/
public function render_icon( Ad $ad ) {
$image = 'adsense-display.svg';
$content = json_decode( wp_unslash( $ad->get_content() ), true );
if ( isset( $content['unitType'] ) ) {
switch ( $content['unitType'] ) {
case 'matched-content':
$image = 'adsense-multiplex.svg';
break;
case 'in-article':
$image = 'adsense-in-article.svg';
break;
case 'in-feed':
$image = 'adsense-in-feed.svg';
break;
}
}
echo '<img src="' . esc_url( ADVADS_BASE_URL ) . '/modules/gadsense/admin/assets/img/' . esc_attr( $image ) . '" width="50" height="50">';
}
/**
* Render additional information in the ad type tooltip on the ad overview page
*
* @param Ad $ad Ad instance.
*/
public function render_ad_type_tooltip( Ad $ad ) {
$content = json_decode( stripslashes( $ad->get_content() ), true );
if ( isset( $content['unitType'] ) ) {
echo esc_html( self::get_ad_type_label( $content['unitType'] ) );
}
}
/**
* Sanitize content field on save
*
* @param string $content ad content.
*
* @return string $content sanitized ad content
* @since 1.0.0
*/
public function sanitize_content( $content = '' ) {
$content = wp_unslash( $content );
$ad_unit = json_decode( $content, true );
if ( empty( $ad_unit ) ) {
$ad_unit = [];
}
// Remove this slotId from unsupported_ads.
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
if ( array_key_exists( 'slotId', $ad_unit ) && array_key_exists( $ad_unit['slotId'], $mapi_options['unsupported_units'] ) ) {
unset( $mapi_options['unsupported_units'][ $ad_unit['slotId'] ] );
update_option( Advanced_Ads_AdSense_MAPI::OPTION_KEY, $mapi_options );
}
return $content;
}
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
global $gadsense;
$ad_args = $this->get_prop( 'ad_args' );
$content = json_decode( stripslashes( $this->get_content() ) );
if (
isset( $ad_args['wp_the_query']['is_404'] ) &&
$ad_args['wp_the_query']['is_404'] &&
! defined( 'ADVADS_ALLOW_ADSENSE_ON_404' )
) {
return '';
}
$output = '';
$db = Advanced_Ads_AdSense_Data::get_instance();
$pub_id = $db->get_adsense_id( $this );
if ( ! isset( $content->unitType ) || empty( $pub_id ) ) {
return $output;
}
// deprecated since the adsbygoogle.js file is now always loaded.
if ( ! isset( $gadsense['google_loaded'] ) || ! $gadsense['google_loaded'] ) {
$gadsense['google_loaded'] = true;
}
// check if passive cb is used.
if ( isset( $gadsense['adsense_count'] ) ) {
++$gadsense['adsense_count'];
} else {
$gadsense['adsense_count'] = 1;
}
// "link" was a static format until AdSense stopped filling them in March 2021. Their responsive format serves as a fallback recommended by AdSense
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$is_static_normal_content = ! in_array( $content->unitType, [ 'responsive', 'link', 'link-responsive', 'matched-content', 'in-article', 'in-feed' ], true );
$output = apply_filters( 'advanced-ads-gadsense-output', false, $this, $pub_id, $content );
if ( false !== $output ) {
return $output;
}
// Prevent output on AMP pages.
if ( Conditional::is_amp() ) {
return '';
}
$output = '';
// Add notice when a link unit is used.
if ( in_array( $content->unitType, [ 'link', 'link-responsive' ], true ) ) {
Advanced_Ads_Ad_Health_Notices::get_instance()->add( 'adsense_link_units_deprecated' );
}
// build static normal content ads first.
if ( $is_static_normal_content ) {
$output .= $this->get_script_tag( $pub_id );
$output .= '<ins class="adsbygoogle" ';
$output .= 'style="display:inline-block;width:' . $this->get_width() . 'px;height:' . $this->get_height() . 'px;" ' . "\n";
$output .= 'data-ad-client="ca-' . $pub_id . '" ' . "\n";
$output .= 'data-ad-slot="' . $content->slotId . '"';
// ad type for static link unit.
if ( 'link' === $content->unitType ) {
$output .= "\n" . 'data-ad-format="link"';
}
$output .= '></ins> ' . "\n";
$output .= '<script> ' . "\n";
$output .= '(adsbygoogle = window.adsbygoogle || []).push({}); ' . "\n";
$output .= '</script>' . "\n";
} else {
/**
* The value of $ad->content->resize should be tested to format the output correctly
*/
$unmodified = $output;
$output = apply_filters( 'advanced-ads-gadsense-responsive-output', $output, $this, $pub_id );
if ( $unmodified === $output ) {
/**
* If the output has not been modified, perform a default responsive output.
* A simple did_action check isn't sufficient, some hooks may be attached and fired but didn't touch the output
*/
$this->append_defaut_responsive_content( $output, $pub_id, $content );
// Remove float setting if this is a responsive ad unit without custom sizes.
unset( $this->wrapper['style']['float'] );
}
}
return $output;
}
/**
* Check if a string looks like an AdSense ad code.
*
* @param string $content The string that need to be checked.
*
* @return boolean
*/
public static function content_is_adsense( $content = '' ) {
return false !== stripos( $content, 'googlesyndication.com' ) &&
( false !== stripos( $content, 'google_ad_client' ) || false !== stripos( $content, 'data-ad-client' ) );
}
/**
* Build AdSense script tag.
*
* @param string $pub_id AdSense publisher ID.
*
* @return string
*/
protected function get_script_tag( $pub_id ) {
return sprintf(
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- don't allow any changes on Google AdSense code.
'<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js%s" crossorigin="anonymous"></script>',
/**
* Filter the output of the publisher ID appended to the AdSense JavaScript Code.
*
* @param boolean
*/
apply_filters( 'advanced-ads-adsense-publisher-id', true ) ? '?client=ca-' . $pub_id : ''
);
}
/**
* Append responsive content
*
* @param string $output Current ad unit code.
* @param string $pub_id AdSense publisher ID.
* @param object $content Ad unit content with all parameters.
*/
protected function append_defaut_responsive_content( &$output, $pub_id, $content ) {
$format = '';
$style = 'display:block;';
switch ( $content->unitType ) {
case 'matched-content':
$format = 'autorelaxed';
break;
case 'link-responsive':
case 'link':
$format = 'link';
break;
case 'in-feed':
$format = 'fluid';
$layout_key = $content->layout_key;
break;
case 'in-article':
$format = 'fluid';
$layout = 'in-article';
$style = 'display:block; text-align:center;';
break;
default:
$format = 'auto';
}
$output .= $this->get_script_tag( $pub_id );
$output .= '<ins class="adsbygoogle" ';
$output .= 'style="' . $style . '" ';
$output .= 'data-ad-client="ca-' . $pub_id . '" ' . "\n";
$output .= 'data-ad-slot="' . $content->slotId . '" ' . "\n";
$output .= isset( $layout ) ? 'data-ad-layout="' . $layout . '"' . "\n" : '';
$output .= isset( $layout_key ) ? 'data-ad-layout-key="' . $layout_key . '"' . "\n" : '';
$output .= 'data-ad-format="';
$output .= $format;
$options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
$fw = ! empty( $options['fullwidth-ads'] ) ? $options['fullwidth-ads'] : 'default';
if ( 'default' !== $fw ) {
$output .= 'enable' === $fw ? '" data-full-width-responsive="true' : '" data-full-width-responsive="false';
}
$output .= '"></ins>' . "\n";
$output .= '<script> ' . "\n";
$output .= apply_filters( 'advanced-ads-gadsense-responsive-adsbygoogle', '(adsbygoogle = window.adsbygoogle || []).push({}); ' . "\n" );
$output .= '</script>' . "\n";
}
}

View File

@@ -0,0 +1,333 @@
<?php
/**
* Handle all report data received from Google
*
* @package Advanced_Ads
*/
/**
* Main class
*/
class AdSense_Report_Data implements Serializable {
/**
* Cached data life span.
*
* @var integer
*/
const CACHE_DURATION = 3600;
/**
* DB option name for report by domain.
*
* @var string
*/
const DOMAIN_OPTION = 'advanced_ads_adsense_report_domain';
/**
* DB option name for report by ad unit.
*
* @var string
*/
const UNIT_OPTION = 'advanced_ads_adsense_report_unit';
/**
* Daily earnings.
*
* @var null|array
*/
private $earnings;
/**
* Report type. 'unit' or 'domain'.
*
* @var string
*/
private $type;
/**
* UNIX timestamp at which the data was obtained from Google.
*
* @var int
*/
private $timestamp = 0;
/**
* Currency used in the report.
*
* @var string
*/
private $currency = '';
/**
* Version of Google AdSense Management API used.
*
* @var string
*/
private $version = '';
/**
* List of domain names found in the report data.
*
* @var array
*/
private $domains = [];
/**
* Instance constructor.
*
* @param string $type report type.
*/
public function __construct( $type = 'unit' ) {
$this->type = $type;
}
/**
* Get all domains.
*
* @return array the domain list.
*/
public function get_domains() {
return $this->domains;
}
/**
* Get the report timestamp.
*
* @return int data timestamp.
*/
public function get_timestamp() {
return $this->timestamp;
}
/**
* Get the currency used in the report.
*
* @return string the currency code.
*/
public function get_currency() {
return $this->currency;
}
/**
* Serialize an instance of this class into a string. For PHP version >= 7.4
*
* @return array
*/
public function __serialize() {
return [
'earnings' => $this->earnings,
'type' => $this->type,
'timestamp' => $this->timestamp,
'currency' => $this->currency,
'domains' => $this->domains,
];
}
/**
* Recreate an instance of this class from a string. For PHP version >= 7.4
*
* @param array $data the array from __serialize.
*
* @return void
*/
public function __unserialize( $data ) {
$this->earnings = $data['earnings'] ?? null;
$this->type = $data['type'] ?? null;
$this->timestamp = $data['timestamp'] ?? 0;
$this->currency = $data['currency'] ?? '';
$this->domains = $data['domains'] ?? [];
}
/**
* Returns serialized object properties. For PHP version < 7.4
*
* @return string the serialized data.
*/
public function serialize() {
return serialize(
[
'earnings' => $this->earnings,
'type' => $this->type,
'timestamp' => $this->timestamp,
'currency' => $this->currency,
'domains' => $this->domains,
]
);
}
/**
* Set object properties from serialized data string. For PHP version < 7.4
*
* @param string $data serilaized data from DB.
*/
public function unserialize( $data ) {
try {
$unwrapped = unserialize( $data );
} catch ( Exception $ex ) {
$unwrapped = [];
}
$this->__unserialize( $unwrapped );
}
/**
* Update object properties and DB record from a API response.
*
* @param array $response API call response from Google.
*/
public function update_data_from_response( $response ) {
$headers = [];
$this->version = $response['api_version'];
$this->timestamp = $response['timestamp'];
foreach ( $response['headers'] as $header ) {
if ( 'METRIC_CURRENCY' === $header['type'] ) {
$this->currency = $header['currencyCode'];
}
$headers[] = $header['name'];
}
$earnings = [];
if ( ! empty( $response['rows'] ) ) {
foreach ( $response['rows'] as $row ) {
$earning = new StdClass();
foreach ( $row['cells'] as $index => $cell ) {
switch ( $headers[ $index ] ) {
case 'DATE':
$earning->date = new DateTimeImmutable( $cell['value'] );
break;
case 'ESTIMATED_EARNINGS':
$earning->estimated_earning = (float) $cell['value'];
break;
default: // "DOMAIN_NAME" or "AD_UNIT_ID".
$earning->{strtolower( $headers[ $index ] )} = $cell['value'];
if ( 'DOMAIN_NAME' === $headers[ $index ] && ! in_array( $cell['value'], $this->domains, true ) ) {
$this->domains[] = $cell['value'];
}
}
}
$earnings[] = $earning;
}
}
$this->earnings = $earnings;
$option_name = 'unit' === $this->type ? self::UNIT_OPTION : self::DOMAIN_OPTION;
// Delete old options entries.
delete_option( 'advanced_ads_adsense_report_DATE_AD_UNIT_CODE_EARNINGS_dashboard' );
delete_option( 'advanced_ads_adsense_report_DATE_DOMAIN_NAME_EARNINGS_dashboard' );
// Save the data instance in DB.
update_option( $option_name, $this->serialize() );
}
/**
* Returns a data object constructed from saved data. Constructs a new one if there is no usable data.
*
* @param string $type report type.
*
* @return AdSense_Report_Data
*/
public static function get_data_from_options( $type ) {
$option_name = 'unit' === $type ? self::UNIT_OPTION : self::DOMAIN_OPTION;
$option = get_option( $option_name, false );
if ( ! $option ) {
return new self( $type );
}
// PHP version < 7.4.
if ( $option instanceof self ) {
return $option;
}
try {
$unserialized = is_serialized( $option ) ? unserialize( $option ) : null;
if ( $unserialized instanceof self ) {
return $unserialized;
}
return new self( $type );
} catch ( Exception $ex ) {
return new self( $type );
}
}
/**
* Checks if cached data need to be updated.
*
* @return bool true if the stored data has not expired yet.
*/
public function is_valid() {
return $this->timestamp + self::CACHE_DURATION > time();
}
/**
* Get the earnings sums for display.
*
* @param string $filter filter sums by a given domain name or ad unit.
*
* @return int[]
*/
public function get_sums( $filter = '' ) {
$today = new DateTimeImmutable();
$yesterday = $today->sub( date_interval_create_from_date_string( '1 day' ) );
$prev7 = $today->sub( date_interval_create_from_date_string( '7 days' ) );
$prev28 = $today->sub( date_interval_create_from_date_string( '28 days' ) );
$sums = [
'today' => 0,
'yesterday' => 0,
'7days' => 0,
'this_month' => 0,
'28days' => 0,
];
// Unit type reports should always have the ad unit id specified.
if ( '' === $filter && 'unit' === $this->type ) {
return $sums;
}
if ( ! is_array( $this->earnings ) || empty( $this->earnings ) ) {
return $sums;
}
foreach ( $this->earnings as $value ) {
if (
( 'unit' === $this->type && false === strpos( $value->ad_unit_id, $filter ) )
|| ( ! empty( $filter ) && 'domain' === $this->type && $filter !== $value->domain_name )
) {
continue;
}
if ( $this->date_ymd( $value->date ) === $this->date_ymd( $today ) ) {
$sums['today'] += $value->estimated_earning;
}
if ( $this->date_ymd( $value->date ) === $this->date_ymd( $yesterday ) ) {
$sums['yesterday'] += $value->estimated_earning;
}
if ( $this->date_ymd( $value->date ) >= $this->date_ymd( $prev7 ) ) {
$sums['7days'] += $value->estimated_earning;
}
if ( $this->date_ymd( $value->date ) >= $this->date_ymd( $prev28 ) ) {
$sums['28days'] += $value->estimated_earning;
}
if ( $value->date->format( 'm' ) === $today->format( 'm' ) ) {
$sums['this_month'] += $value->estimated_earning;
}
}
return $sums;
}
/**
* Get an integer representation of a DateTime object to be used in date comparison.
*
* @param DateTimeInterface $date the date object.
*
* @return int
*/
private function date_ymd( $date ) {
if ( $date instanceof DateTimeInterface ) {
return (int) $date->format( 'Ymd' );
}
return 0;
}
}

View File

@@ -0,0 +1,176 @@
<?php
/**
* Class Advanced_Ads_AdSense_Report
*
* Displays AdSense earnings on the ad overview page or the ad edit page.
*/
class Advanced_Ads_AdSense_Report {
/**
* Domain name or ad unit to filter data with before display.
*
* @var string
*/
private $filter;
/**
* Report type. 'unit' or 'domain'.
*
* @var string
*/
private $type;
/**
* Object representing the current report data.
*
* @var AdSense_Report_Data
*/
private $data_object;
/**
* Error from the last attempt to call Google.
*
* @var string
*/
private $last_api_error_message;
/**
* Instance constructor.
*
* @param string $type report type.
* @param string $filter report filter.
*/
public function __construct( $type = 'unit', $filter = '' ) {
$this->type = $type;
if ( 'domain' === $type && ! empty( $filter ) ) {
update_option( 'advanced-ads-adsense-dashboard-filter', $filter );
// Backward compatibility: "*" was used to display data for all domains if API version prior to 2.0.
if ( '*' === $filter ) {
$filter = '';
}
}
$this->filter = $filter;
$this->data_object = AdSense_Report_Data::get_data_from_options( $type );
}
/**
* Tries to get fresh data from Google.
*
* @return bool true if we got fresh data.
*/
public function refresh_report() {
$api_helper = new Advanced_Ads_AdSense_Report_Api( $this->type );
$error = [];
if ( $api_helper->has_token() ) {
$response = $api_helper->call_google();
if ( true === $response['status'] ) {
$this->data_object->update_data_from_response( $response['response_body'] );
return true;
}
if ( isset( $response['msg'] ) ) {
$this->last_api_error_message = $response['msg'];
return false;
}
}
if ( $api_helper->has_token_error() ) {
$error = $api_helper->get_token_error();
}
if ( isset( $error['msg'] ) ) {
$this->last_api_error_message = $error['msg'];
return false;
}
if ( isset( $error['raw'] ) ) {
$this->last_api_error_message = $error['raw'];
return false;
}
if ( empty( $this->last_api_error_message ) ) {
$this->last_api_error_message = __( 'No valid tokens', 'advanced-ads' );
}
return false;
}
/**
* Retrieve the error message from the last API call.
*
* @return string Error message from the last API call.
*/
public function get_last_api_error() {
if ( empty( $this->last_api_error_message ) ) {
return '';
}
return $this->last_api_error_message;
}
/**
* Returns the report data object.
*
* @return AdSense_Report_Data
*/
public function get_data() {
return $this->data_object;
}
/**
* Build an return the HTML markup for display.
*
* @return string the final markup.
*/
public function get_markup() {
if ( ! $this->get_data()->is_valid() ) {
return '<p style="text-align:center;"><span class="report-need-refresh spinner advads-ad-parameters-spinner advads-spinner"></span></p>';
}
ob_start();
$report_filter = $this->filter;
$report_domains = $this->data_object->get_domains();
$sums = $this->data_object->get_sums( $this->filter );
$earning_cells = '';
foreach ( $sums as $index => $sum ) {
$earning_cells .= $this->get_earning_cell( $sum, $index );
}
require_once GADSENSE_BASE_PATH . '/admin/views/adsense-report.php';
return ob_get_clean();
}
/**
* Build and return the HTML markup for a given period.
*
* @param float $sum the earning for that period.
* @param string $index the period identifier.
*
* @return string HTML of the individual cell.
*/
private function get_earning_cell( $sum, $index ) {
$period_strings = [
'today' => esc_html__( 'Today', 'advanced-ads' ),
'yesterday' => esc_html__( 'Yesterday', 'advanced-ads' ),
/* translators: 1: The number of days. */
'7days' => sprintf( esc_html__( 'Last %1$d days', 'advanced-ads' ), 7 ),
'this_month' => esc_html__( 'This Month', 'advanced-ads' ),
/* translators: 1: The number of days. */
'28days' => sprintf( esc_html__( 'Last %1$d days', 'advanced-ads' ), 28 ),
];
$markup = '<div class="advads-flex1 advads-stats-box"><div>' . $period_strings[ $index ] . '</div>';
$markup .= '<div class="advads-stats-box-main">';
$markup .= number_format_i18n( ceil( 100 * $sum ) / 100, 2 );
$markup .= ' ' . $this->get_data()->get_currency();
$markup .= '</div></div>';
return $markup;
}
}

View File

@@ -0,0 +1,162 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
use AdvancedAds\Constants;
use AdvancedAds\Entities;
/**
* Adsense data class.
*/
class Advanced_Ads_AdSense_Data {
/**
* Singleton instance
*
* @var Advanced_Ads_AdSense_Data
*/
private static $instance;
/**
* Hold options
*
* @var array
*/
private $options;
/**
* Hold resizing data
*
* @var array
*/
private $resizing;
/**
* The constructor.
*/
private function __construct() {
$options = get_option( GADSENSE_OPT_NAME, [] );
// Set defaults.
if ( ! isset( $options['adsense-id'] ) ) {
$options['adsense-id'] = '';
update_option( GADSENSE_OPT_NAME, $options );
}
$this->options = wp_parse_args(
$options,
[
'background' => false,
'page-level-enabled' => false,
]
);
// Resizing method for responsive ads.
$this->resizing = [
'auto' => __( 'Auto', 'advanced-ads' ),
];
}
/**
* GETTERS
*/
public function get_options() {
return $this->options;
}
/**
* Get adsense id
*
* @param Ad|null $ad Ad instance.
*
* @return string
*/
public function get_adsense_id( $ad = null ) {
if ( ! empty( $ad ) && $ad->is_type( 'adsense' ) ) {
$ad_content = json_decode( $ad->get_content() );
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
if ( $ad_content && isset( $ad_content->pubId ) && ! empty( $ad_content->pubId ) ) {
return $ad_content->pubId;
}
// phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}
return trim( $this->options['adsense-id'] );
}
/**
* Get limit per page
*
* @deprecated 1.47.0
* @deprecated The feature is deprecated by AdSense since 2019
*
* @return int
*/
public function get_limit_per_page() {
_deprecated_function( __METHOD__, '1.47.0' );
return 0;
}
/**
* Get responsive sizing
*
* @return array
*/
public function get_responsive_sizing() {
$this->resizing = apply_filters( 'advanced-ads-gadsense-responsive-sizing', $this->resizing );
return $this->resizing;
}
/**
* Get class instance
*
* @return Advanced_Ads_AdSense_Data
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new Advanced_Ads_AdSense_Data();
}
return self::$instance;
}
/**
* ISSERS/HASSERS
*/
public function is_page_level_enabled() {
return $this->options['page-level-enabled'];
}
/**
* Is setup
*
* @return boolean
*/
public function is_setup() {
if ( isset( $this->options ) && is_array( $this->options ) && isset( $this->options['adsense-id'] ) && $this->options['adsense-id'] ) {
$adsense_id = $this->get_adsense_id();
if ( $adsense_id ) {
return Advanced_Ads_AdSense_MAPI::has_token( $adsense_id );
}
}
return false;
}
/**
* Whether to hide the AdSense stats metabox.
*
* @return bool
*/
public function is_hide_stats() {
global $post;
if ( $post instanceof WP_Post && Constants::POST_TYPE_AD === $post->post_type ) {
$the_ad = wp_advads_get_ad( $post->ID );
if ( ! $the_ad->is_type( 'adsense' ) ) {
return true;
}
}
return isset( $this->options['hide-stats'] );
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,573 @@
<?php // phpcs:ignoreFile
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Compatibility\Compatibility;
/**
* Class Advanced_Ads_Network_Adsense
*/
class Advanced_Ads_Network_Adsense extends Advanced_Ads_Ad_Network {
/**
* An array containing all the AdSense status codes that flag an {$link Advanced_Ads_Ad_Network_Ad_Unit} ad unit as active
* for downward compatibility with PHP < 5.6 the const had to be changed to static field. you can revert to const when PHP5 support is FINALLY dropped
*
* @var array
*/
private static $status_codes_active = [ 'ACTIVE', 'NEW' ];
/**
* A globally usable instance, that will be created when calling {$link Advanced_Ads_Ad_Network#get_instance) for the first time
*
* @var Advanced_Ads_Ad_Type_Adsense
*/
private static $instance;
/**
* AdSense options handling class.
*
* @var Advanced_Ads_AdSense_Data
*/
private $data;
/**
* The AdSense networks settings section ID
*
* @var string
*/
protected $settings_section_id;
/**
* Instance of Advanced_Ads_Network_Adsense
*
* @return Advanced_Ads_Ad_Type_Adsense|Advanced_Ads_Network_Adsense
*/
final public static function get_instance() {
if ( ! self::$instance ) {
self::$instance = new Advanced_Ads_Network_Adsense();
}
return self::$instance;
}
/**
* Advanced_Ads_Network_Adsense constructor.
*/
public function __construct() {
parent::__construct( 'adsense', 'AdSense' );
$this->data = Advanced_Ads_AdSense_Data::get_instance();
// Adsense does not use the default generated settings section id. overwrite it with the old value.
$this->settings_section_id = 'advanced_ads_adsense_setting_section';
add_action( 'advanced_ads_settings_before_form', [ $this, 'render_before_form' ], 10, 2 );
}
/**
* Render before form.
*
* @param string $tab_id the ID of the current tab.
* @param array $tab the current tab.
*/
public function render_before_form( $tab_id, $tab ) {
if ( 'adsense' === $tab_id && ! empty( $this->data->get_adsense_id() ) ) {
$_notice = 'adsense_subscribe';
if ( Advanced_Ads_Admin_Notices::get_instance()->can_display( $_notice ) && Conditional::user_can_subscribe( 'nl_first_steps' ) ) {
$text = sprintf(
/* translators: %s: number of add-ons. */
__( 'Subscribe to our free email course for Google AdSense, receive our newsletter for periodic tutorials, and get %s for Advanced Ads.', 'advanced-ads' ),
'<strong>' . __( '2 free add-ons', 'advanced-ads' ) . '</strong>'
);
include ADVADS_ABSPATH . '/admin/views/notices/inline.php';
}
}
}
/**
* Register settings to Advanced Ads > Settings > AdSense
*
* @param string $hook settings page hook.
* @param string $section_id settings section id.
*/
protected function register_settings( $hook, $section_id ) {
// Add setting field to disable ads.
add_settings_field(
'adsense-id',
__( 'AdSense account', 'advanced-ads' ),
[ $this, 'render_settings_adsense_id' ],
$hook,
$section_id
);
// Activate AdSense verification code and Auto ads (previously Page-Level ads).
add_settings_field(
'adsense-page-level',
__( 'Auto ads', 'advanced-ads' ),
[ $this, 'render_settings_adsense_page_level' ],
$hook,
$section_id
);
// AdSense anchor ad on top of pages.
// Only show this field if selected, otherwise use new auto ads code.
if ( isset( $this->data->get_options()['top-anchor-ad'] ) ) {
add_settings_field(
'top_anchor_ad',
__( 'Auto ads', 'advanced-ads' ) . ':&nbsp;' . __( 'Disable top anchor ad', 'advanced-ads' ),
[ $this, 'render_settings_adsense_top_anchor_ad' ],
$hook,
$section_id
);
}
// Hide AdSense stats in the backend.
add_settings_field(
'hide_stats',
__( 'Disable stats', 'advanced-ads' ),
[ $this, 'render_settings_adsense_hide_stats' ],
$hook,
$section_id
);
// Disable AdSense violation warnings.
add_settings_field(
'adsense-warnings-disable',
__( 'Disable violation warnings', 'advanced-ads' ),
[ $this, 'render_settings_adsense_warnings_disable' ],
$hook,
$section_id
);
// Show AdSense widget on WP Dashboard.
add_settings_field(
'adsense_wp_dashboard',
__( 'Show AdSense Earnings', 'advanced-ads' ),
[ $this, 'render_settings_adsense_wp_dashboard' ],
$hook,
$section_id
);
add_settings_field(
'adsense-background',
__( 'Transparent background', 'advanced-ads' ),
[ $this, 'render_settings_adsense_background' ],
$hook,
$section_id
);
add_settings_field(
'adsense-full-width',
__( 'Full width responsive ads on mobile', 'advanced-ads' ),
[ $this, 'render_settings_adsense_fullwidth' ],
$hook,
'advanced_ads_adsense_setting_section'
);
}
/**
* Render AdSense settings section
*/
public function render_settings_section_callback() {
// For whatever purpose there might come.
}
/**
* Render AdSense management api setting
*/
public function render_settings_management_api() {
require_once GADSENSE_BASE_PATH . 'admin/views/mapi-settings.php';
}
/**
* Render AdSense id setting
*/
public function render_settings_adsense_id() {
require_once GADSENSE_BASE_PATH . 'admin/views/adsense-account.php';
}
/**
* Render top anchor ad setting
*/
public function render_settings_adsense_top_anchor_ad() {
$options = $this->data->get_options();
$anchor_ad = isset( $options['top-anchor-ad'] ) ? $options['top-anchor-ad'] : '';
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[top-anchor-ad]" value="1" <?php checked( $anchor_ad ); ?> />
<?php esc_html_e( 'Enable this box if you dont want Google Auto ads to place anchor ads at the top of your page.', 'advanced-ads' ); ?>
<?php WordPress::show_deprecated_notice('top Anchor Ad'); ?>
</label>
<?php
}
/**
* Render setting to hide AdSense stats showing in the backend
*/
public function render_settings_adsense_hide_stats() {
$options = $this->data->get_options();
$hide_stats = isset( $options['hide-stats'] );
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[hide-stats]" value="1" <?php checked( $hide_stats ); ?> />
<?php esc_html_e( 'Enable this option to stop loading stats from AdSense into your WordPress backend.', 'advanced-ads' ); ?>
</label>
<?php
}
/**
* Render setting to hide AdSense stats showing in the backend
*/
public function render_settings_adsense_wp_dashboard() {
$options = $this->data->get_options();
$show_widget = isset( $options['adsense-wp-widget'] );
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[adsense-wp-widget]" value="1" <?php checked( $show_widget ); ?> />
<?php esc_html_e( 'Show Earnings widget on the WordPress dashboard.', 'advanced-ads' ); ?>
</label>
<?php
}
/**
* Render page-level ads setting
*
* @since 1.6.9
*/
public function render_settings_adsense_page_level() {
$options = $this->data->get_options();
$page_level = $options['page-level-enabled'];
?>
<label><input type="checkbox" name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[page-level-enabled]" value="1" <?php checked( $page_level ); ?> />
<?php esc_attr_e( 'Insert the AdSense header code to enable Auto ads and verify your website.', 'advanced-ads' ); ?>
</label>
<ul>
<li><a href="https://wpadvancedads.com/adsense-auto-ads-wordpress/?utm_source=advanced-ads&utm_medium=link&utm_campaign=settings-adsense-specific-pages#Display_AdSense_Auto_Ads_only_on_specific_pages" target="_blank"><?php esc_attr_e( 'Display Auto ads only on specific pages', 'advanced-ads' ); ?></a></li>
<li><a href="https://wpadvancedads.com/adsense-in-random-positions-auto-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=backend-autoads-ads" target="_blank"><?php esc_attr_e( 'Why are ads appearing in random positions?', 'advanced-ads' ); ?></a></li>
<?php
if ( ! empty( $options['adsense-id'] ) ) :
?>
<li><a href="https://www.google.com/adsense/new/u/0/<?php echo esc_attr( $options['adsense-id'] ); ?>/myads/auto-ads" target="_blank">
<?php
/* translators: this is the text for a link to a sub-page in an AdSense account */
esc_attr_e( 'Adjust Auto ads options', 'advanced-ads' );
?>
</a></li>
<?php
endif;
?>
</ul>
<?php if ( Compatibility::borlabs_cookie_adsense_auto_ads_code_exists() ) : ?>
<p class="advads-notice-inline advads-error">
<?php require GADSENSE_BASE_PATH . 'admin/views/borlabs-cookie-auto-ads-warning.php'; ?>
</p>
<?php endif; ?>
<?php
self::render_settings_adsense_amp();
do_action( 'advanced-ads-settings-adsense-below-auto-ads-option' );
}
/**
* Render Adsense AMP setting fields.
*/
public function render_settings_adsense_amp() {
// AMP Auto ads was removed from Responsive add-on version 1.10.0
if ( defined( 'AAR_VERSION' ) && 1 === version_compare( '1.10.0', AAR_VERSION ) ) {
return;
}
$adsense_options = Advanced_Ads_AdSense_Data::get_instance()->get_options();
$auto_ads_enabled = ! empty( $adsense_options['amp']['auto_ads_enabled'] );
$option_name = GADSENSE_OPT_NAME . '[amp]';
include GADSENSE_BASE_PATH . 'admin/views/settings/amp-auto-ads.php';
}
/**
* Render AdSense violation warnings setting
*
* @since 1.6.9
*/
public function render_settings_adsense_warnings_disable() {
$options = $this->data->get_options();
$disable_violation_warnings = isset( $options['violation-warnings-disable'] ) ? 1 : 0;
?>
<label><input type="checkbox" name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[violation-warnings-disable]" value="1" <?php checked( 1, $disable_violation_warnings ); ?> />
<?php esc_html_e( 'Disable warnings about potential violations of the AdSense terms.', 'advanced-ads' ); ?></label>
<p class="description">
<?php
printf(
wp_kses(
/* translators: %s is a URL. */
__( 'Our <a href="%s" target="_blank">Ad Health</a> feature monitors if AdSense is implemented correctly on your site. It also considers ads not managed with Advanced Ads. Enable this option to remove these checks', 'advanced-ads' ),
[
'a' => [
'href' => true,
'target' => true,
],
]
),
'https://wpadvancedads.com/manual/ad-health/?utm_source=advanced-ads&utm_medium=link&utm_campaign=backend-autoads-ads'
);
?>
</p>
<?php
}
/**
* Render transparent background setting.
*/
public function render_settings_adsense_background() {
$options = $this->data->get_options();
$background = $options['background'];
?>
<label>
<input type="checkbox" name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[background]" value="1" <?php checked( $background ); ?> />
<?php esc_html_e( 'Enable this option in case your theme adds an unfortunate background color to AdSense ads.', 'advanced-ads' ); ?>
</label>
<?php
}
/**
* Render full width ads setting.
*/
public function render_settings_adsense_fullwidth() {
$options = $this->data->get_options();
$fw = ! empty( $options['fullwidth-ads'] ) ? $options['fullwidth-ads'] : 'default';
?>
<select name="<?php echo esc_attr( GADSENSE_OPT_NAME ); ?>[fullwidth-ads]">
<option value="default" <?php selected( $fw, 'default' ); ?>><?php esc_html_e( 'default', 'advanced-ads' ); ?></option>
<option value="enable" <?php selected( $fw, 'enable' ); ?>><?php esc_html_e( 'enable', 'advanced-ads' ); ?></option>
<option value="disable" <?php selected( $fw, 'disable' ); ?>><?php esc_html_e( 'disable', 'advanced-ads' ); ?></option>
</select>
<p class="description">
<?php
echo wp_kses(
sprintf(
/* translators: %s is a URL. */
__( "Whether your responsive ad unit may expand to <a href='%s' target='blank'>use the full width</a> of your visitor's mobile device screen", 'advanced-ads' ),
esc_url( 'https://support.google.com/adsense/answer/7445870' )
),
[
'a' => [
'href' => true,
'target' => true,
],
]
);
?>
</p>
<?php
}
/**
* Sanitize AdSense settings
*
* @param array $options all the options.
*/
protected function sanitize_settings( $options ) {
// Sanitize whatever option one wants to sanitize.
if ( isset( $options['adsense-id'] ) && '' !== $options['adsense-id'] ) {
// Remove "ca-" prefix if it was added by the user.
if ( 0 === strpos( $options['adsense-id'], 'ca-' ) ) {
$options['adsense-id'] = str_replace( 'ca-', '', $options['adsense-id'] );
}
// Trim publisher id.
$options['adsense-id'] = trim( $options['adsense-id'] );
}
return $options;
}
/**
* Save publisher id from new ad unit if not given in main options
*
* @param Ad $ad Ad instance.
* @param array $post_data Post data array.
*
* @return void
*/
public function sanitize_ad_settings( Ad $ad, $post_data ): void {
if ( ! $ad->is_type( 'adsense' ) ) {
return;
}
// Save AdSense publisher ID if there is no one stored yet.
if ( ! empty( $post_data['adsense-pub-id'] ) ) {
$adsense_options = get_option( 'advanced-ads-adsense', [] );
if ( empty( $adsense_options['adsense-id'] ) ) {
$adsense_options['adsense-id'] = $post_data['adsense-pub-id'];
update_option( 'advanced-ads-adsense', $adsense_options );
}
}
$ad->unset_prop( 'adsense-pub-id' );
}
/**
* Return ad units loaded through the API.
*
* @return array
*/
public function get_external_ad_units() {
$db = Advanced_Ads_AdSense_Data::get_instance();
$adsense_id = trim( $db->get_adsense_id() );
$units = [];
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
if (
isset( $mapi_options['ad_codes'] )
&& isset( $mapi_options['accounts'] )
&& isset( $mapi_options['accounts'][ $adsense_id ] )
&& isset( $mapi_options['accounts'][ $adsense_id ]['ad_units'] )
) {
$ad_codes = $mapi_options['ad_codes'];
foreach ( $mapi_options['accounts'][ $adsense_id ]['ad_units'] as $id => $raw ) {
$ad_unit = new Advanced_Ads_Ad_Network_Ad_Unit( $raw );
$ad_unit->id = $id;
$ad_unit->slot_id = isset( $raw['code'] ) ? $raw['code'] : '-';
$ad_unit->name = isset( $raw['name'] ) ? $raw['name'] : '-';
// phpcs:ignore
$ad_unit->active = isset( $raw['status'] ) && in_array( $raw['status'], self::$status_codes_active );
if ( isset( $ad_codes[ $id ] ) ) {
$ad_unit->code = $ad_codes[ $id ];
}
if ( isset( $raw['contentAdsSettings'] ) ) {
if ( isset( $raw['contentAdsSettings']['type'] ) ) {
$ad_unit->display_type = $raw['contentAdsSettings']['type'];
$ad_unit->display_type = Advanced_Ads_AdSense_MAPI::format_ad_data( $ad_unit, 'type' );
}
if ( isset( $raw['contentAdsSettings']['size'] ) ) {
$ad_unit->display_size = $raw['contentAdsSettings']['size'];
$ad_unit->display_size = Advanced_Ads_AdSense_MAPI::format_ad_data( $ad_unit, 'size' );
}
}
$units[] = $ad_unit;
}
}
return $units;
}
/**
* Render the list of ads loaded through the API.
*
* @param bool $hide_idle_ads true to hide inactive ads.
* @param null $ad_unit_id ID of the ad unit.
*
* @return mixed|void
*/
public function print_external_ads_list( $hide_idle_ads = true, $ad_unit_id = null ) {
Advanced_Ads_AdSense_Admin::get_mapi_ad_selector( $hide_idle_ads );
}
/**
* Whether the loaded AdSense ad is supported through the API.
* at the time we wrote this, native ad formats like In-article, In-feed and Matched Content are not supported.
*
* @param object $ad_unit ad unit object.
*
* @return bool
*/
public function is_supported( $ad_unit ) {
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
$supported = ! array_key_exists( $ad_unit->id, $mapi_options['unsupported_units'] );
if ( ! $supported ) {
$supported = array_key_exists( $ad_unit->id, $mapi_options['ad_codes'] );
}
return $supported;
}
/**
* Update the list of external ad units.
*/
public function update_external_ad_units() {
Advanced_Ads_AdSense_MAPI::get_instance()->ajax_get_adUnits();
}
/**
* If the AdSense account is connected.
*
* @return bool
*/
public function is_account_connected() {
return Advanced_Ads_AdSense_Data::get_instance()->is_setup();
}
/**
* Return path to module-specific JavaScript.
*
* @return string
*/
public function get_javascript_base_path() {
return GADSENSE_BASE_URL . 'admin/assets/js/adsense.js';
}
/**
* JavaScript data to print in the source code.
*
* @inheritDoc
*
* @param array $data data to be printed.
* @return array
*/
public function append_javascript_data( &$data ) {
$pub_id = Advanced_Ads_AdSense_Data::get_instance()->get_adsense_id();
$data['pubId'] = $pub_id;
$data['connected'] = $this->is_account_connected();
$data['ad_types'] = [
'matched_content' => _x( 'Multiplex', 'AdSense ad type', 'advanced-ads' ),
'in_article' => _x( 'In-article', 'AdSense ad type', 'advanced-ads' ),
'in_feed' => _x( 'In-feed', 'AdSense ad type', 'advanced-ads' ),
'display' => _x( 'Display', 'AdSense ad type', 'advanced-ads' ),
'link' => _x( 'Link', 'AdSense ad type', 'advanced-ads' ),
];
return $data;
}
/**
* If the ad also has a manual ad setup option.
*
* @return bool
*/
public function supports_manual_ad_setup() {
return true;
}
/**
* Get the ad unit associated with a given ad ID.
*
* @param int $ad_id The ID of the ad.
* @return object|null The ad unit object associated with the given ad ID, or null if not found.
*/
function get_ad_unit( $ad_id ){
$adense_ad = wp_advads_get_ad( $ad_id );
// Early bail!!
if ( ! $adense_ad || ! $adense_ad->is_type( 'adsense' ) || empty( $adense_ad->get_content() ) ) {
return null;
}
$ad_units = $this->get_external_ad_units();
if ( empty( $ad_units ) ) {
return null;
}
$json_content = json_decode( $adense_ad->get_content() );
$unit_code = $json_content->slotId ?? null;
foreach( $ad_units as $ad_unit ) {
if( $ad_unit->slot_id === $unit_code){
return $ad_unit;
}
}
return null;
}
}

View File

@@ -0,0 +1,200 @@
<?php
/**
* This class represents the "Adsense" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.4.0
*/
namespace AdvancedAds\Adsense\Types;
use Advanced_Ads_Ad_Type_Adsense;
use Advanced_Ads_AdSense_Data;
use Advanced_Ads_AdSense_MAPI;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type Adsense.
*/
class Adsense implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'adsense';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Advanced_Ads_Ad_Type_Adsense::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'AdSense ad', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Use ads from your Google AdSense account', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/adsense.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
/**
* Output for the ad parameters metabox
*
* @since 1.4
* @param Ad $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
// TODO: THIS IS JUST A QUICK AND DIRTY HACK. Create a dedicated method to handle this properly.
?>
<script>
jQuery( function () {
<?php
$mapi_options = Advanced_Ads_AdSense_MAPI::get_option();
$json_ad_codes = wp_json_encode( $mapi_options['ad_codes'] );
?>
const adsense = new AdvancedAdsNetworkAdsense(<?php echo $json_ad_codes; // phpcs:ignore ?>)
AdvancedAdsAdmin.AdImporter.setup( adsense )
} )
</script>
<?php
$options = $ad->get_data();
$content = $ad->get_content() ?? '';
$unit_id = '';
$unit_pubid = '';
$unit_code = '';
$unit_type = 'responsive';
$unit_width = 0;
$unit_height = 0;
$json_content = '';
$unit_resize = '';
$extra_params = [
'default_width' => '',
'default_height' => '',
'at_media' => [],
];
$db = Advanced_Ads_AdSense_Data::get_instance();
$pub_id = trim( $db->get_adsense_id( $ad ) );
// check pub_id for errors.
$pub_id_errors = false;
if ( '' !== $pub_id && 0 !== strpos( $pub_id, 'pub-' ) ) {
$pub_id_errors = __( 'The Publisher ID has an incorrect format. (must start with "pub-")', 'advanced-ads' );
}
global $external_ad_unit_id, $use_dashicons, $closeable;
$closeable = true;
$use_dashicons = false;
$external_ad_unit_id = '';
if ( trim( $content ) !== '' ) {
$json_content = stripslashes( $content );
// get json content striped by slashes.
$content = json_decode( stripslashes( $content ) );
if ( isset( $content->unitType ) ) {
$content->json = $json_content;
$unit_type = $content->unitType;
$unit_code = $content->slotId;
$unit_pubid = ! empty( $content->pubId ) ? $content->pubId : $pub_id;
$layout = isset( $content->layout ) ? $content->layout : '';
$layout_key = isset( $content->layout_key ) ? $content->layout_key : '';
if ( 'responsive' !== $content->unitType && 'link-responsive' !== $content->unitType && 'matched-content' !== $content->unitType ) {
// Normal ad unit.
$unit_width = $ad->get_width();
$unit_height = $ad->get_height();
} else {
// Responsive && multiplex ads.
$unit_resize = ( isset( $content->resize ) ) ? $content->resize : 'auto';
if ( 'auto' !== $unit_resize ) {
$extra_params = apply_filters( 'advanced-ads-gadsense-ad-param-data', $extra_params, $content, $ad );
}
}
if ( ! empty( $unit_pubid ) ) {
$unit_id = 'ca-' . $unit_pubid . ':' . $unit_code;
}
$external_ad_unit_id = $unit_id;
}
}
if ( '' === trim( $pub_id ) && '' !== trim( $unit_code ) ) {
$pub_id_errors = __( 'Your AdSense Publisher ID is missing.', 'advanced-ads' );
}
$default_template = GADSENSE_BASE_PATH . 'admin/views/adsense-ad-parameters.php';
/**
* Inclusion of other UI template is done here. The content is passed in order to allow the inclusion of different
* templates file, depending of the ad. It's up to the developer to verify that $content is not an empty
* variable (which is the case for a new ad).
*
* Inclusion of .js and .css files for the ad creation/editon page are done by another hook. See
* 'advanced-ads-gadsense-ad-param-script' and 'advanced-ads-gadsense-ad-param-style' in "../admin/class-gadsense-admin.php".
*/
$template = apply_filters( 'advanced-ads-gadsense-ad-param-template', $default_template, $content );
require $template;
// phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}
}

View File

@@ -0,0 +1,32 @@
<?php // phpcs:ignoreFile
if ( class_exists( 'Advanced_Ads', false ) ) {
define( 'GADSENSE_BASE_PATH', plugin_dir_path( __FILE__ ) );
define( 'GADSENSE_BASE_URL', plugins_url( basename( ADVADS_ABSPATH ) . '/modules/' . basename( GADSENSE_BASE_PATH ) . '/' ) );
define( 'GADSENSE_OPT_NAME', ADVADS_SLUG . '-adsense' );
function gadsense_init() {
Advanced_Ads_AdSense_Data::get_instance();
if ( is_admin() ) {
Advanced_Ads_AdSense_MAPI::get_instance();
}
if ( ! wp_doing_ajax() && is_admin() ) {
Advanced_Ads_AdSense_Admin::get_instance();
} else {
Advanced_Ads_AdSense_Public::get_instance();
}
$network = Advanced_Ads_Network_Adsense::get_instance();
$network->register();
add_action(
'advanced-ads-ad-types-manager',
function ( $manager ) {
$manager->register_type( \AdvancedAds\Adsense\Types\Adsense::class );
}
);
}
add_action( 'advanced-ads-plugin-loaded', 'gadsense_init' );
}

View File

@@ -0,0 +1,214 @@
<?php // phpcs:ignore WordPress.Files.FileName
use AdvancedAds\Utilities\Conditional;
/**
* Class Advanced_Ads_AdSense_Public.
*/
class Advanced_Ads_AdSense_Public {
/**
* AdSense account related data
*
* @var Advanced_Ads_AdSense_Data
*/
private $data;
/**
* Instance of Advanced_Ads_AdSense_Public
*
* @var Advanced_Ads_AdSense_Public
*/
private static $instance;
/**
* Advanced_Ads_AdSense_Public constructor.
*/
private function __construct() {
$this->data = Advanced_Ads_AdSense_Data::get_instance();
add_action( 'wp_head', [ $this, 'inject_header' ], 20 );
// Fires before cache-busting frontend is initialized and tracking method is set.
add_action( 'wp', [ $this, 'inject_amp_code' ], 20 );
}
/**
* Get singleton instance.
*
* @return self
*/
public static function get_instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Print data in the head tag on the front end.
*/
public function inject_header() {
$options = $this->data->get_options();
// Inject CSS to make AdSense background transparent.
if ( ! empty( $options['background'] ) ) {
// Some themes not only get the background wrong, but also add some padding to ins element.
echo '<style>ins.adsbygoogle { background-color: transparent; padding: 0; }</style>';
}
if ( Conditional::is_ad_disabled() || Conditional::is_amp() ) {
return;
}
$privacy = Advanced_Ads_Privacy::get_instance();
$privacy_options = $privacy->options();
$privacy_enabled = $privacy->get_state() !== 'not_needed';
$npa_enabled = ( ! empty( $privacy_options['enabled'] ) && 'custom' === $privacy_options['consent-method'] ) && ! empty( $privacy_options['show-non-personalized-adsense'] );
// Show non-personalized Adsense ads if non-personalized ads are enabled and consent was not given.
if ( $privacy_enabled && $npa_enabled ) {
echo '<script>';
// If the page is not from a cache.
if ( $privacy->get_state() === 'unknown' ) {
echo '(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=1;';
}
// If the page is from a cache, wait until 'advads.privacy' is available. Execute before cache-busting.
echo '( window.advanced_ads_ready || jQuery( document ).ready ).call( null, function() {
var state = ( advads.privacy ) ? advads.privacy.get_state() : "";
var use_npa = ( state === "unknown" ) ? 1 : 0;
(adsbygoogle=window.adsbygoogle||[]).requestNonPersonalizedAds=use_npa;
} )';
echo '</script>';
}
if ( ! apply_filters( 'advanced-ads-can-display-ads-in-header', true ) ) {
return;
}
$pub_id = trim( $this->data->get_adsense_id() );
if ( $pub_id && isset( $options['page-level-enabled'] ) && $options['page-level-enabled'] ) {
$pub_id = $this->data->get_adsense_id();
$client_id = 'ca-' . $pub_id;
$top_anchor = isset( $options['top-anchor-ad'] ) && $options['top-anchor-ad'];
$top_anchor_code = sprintf(
'(adsbygoogle = window.adsbygoogle || []).push({
google_ad_client: "%s",
enable_page_level_ads: true,
overlays: {bottom: true}
});',
esc_attr( $client_id )
);
/**
* Filter the output of the publisher ID appended to the AdSense JavaScript Code.
*
* @param boolean
*/
$add_publisher_id = apply_filters( 'advanced-ads-adsense-publisher-id', true );
$script_src = add_query_arg(
[ 'client' => $add_publisher_id ? esc_attr( $client_id ) : false ],
'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js'
);
/**
* Allows to override the page-level code.
*
* The Pro add-on uses this filter to inject a drop-in replacement for the page-level header code.
*
* @param string $code Existing page level code.
* @param array $parameters {
* Parameters of the AdSense code.
*
* @type string $client_id The Google AdSense client ID.
* @type bool $top_anchor AdSense anchor ad on top of pages.
* @type string $top_anchor_code The code for top anchor ads.
* @type string $script_src AdSense script url.
* }
*/
$custom_code = apply_filters(
'advanced-ads-gadsense-page-level-code',
'',
compact( [ 'client_id', 'top_anchor', 'top_anchor_code', 'script_src' ] )
);
if ( $custom_code ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- the snippet has already been escaped.
echo $custom_code;
return;
}
// inject page-level header code.
include GADSENSE_BASE_PATH . 'public/templates/page-level.php';
}
}
/**
* Handle AdSense AMP code
*/
public function inject_amp_code() {
// for non-AMP pages we do this on the `template_redirect` hook, this has not fired yet.
wp_advads()->frontend->run_checks();
if (
Conditional::is_ad_disabled()
// check if this an AMP page, we're inside `wp` action so it's safe to use.
|| ! Conditional::is_amp()
) {
return;
}
// The is_amp_endpoint function is used for multiple plugins.
if ( function_exists( 'is_amp_endpoint' ) ) {
$adsense_data = Advanced_Ads_AdSense_Data::get_instance();
$adsense_options = $adsense_data->get_options();
// AMP Auto ads was removed from Responsive add-on version 1.10.0.
if (
( defined( 'AAR_VERSION' ) && 1 === version_compare( '1.10.0', AAR_VERSION ) ) ||
empty( $adsense_options['amp']['auto_ads_enabled'] )
) {
return;
}
// Adds the AdSense Auto ads AMP code to the page (head) in "Reader" mode.
add_action( 'amp_post_template_data', [ $this, 'add_auto_ads_amp_head_script' ] );
// SmartMag theme (http://theme-sphere.com/smart-mag/documentation/).
add_action( 'bunyad_amp_pre_main', [ $this, 'add_auto_ads_amp_body_script' ] );
/**
* Add AMP Auto ads body code to footer for `AMP` plugin ( https://wordpress.org/plugins/amp/ )
*
* Adds the Auto ads `body` tag to `wp_footer` because there is no WordPress right hook after `body`
* The AdSense Auto ads code is added automatically to the `head` section using the amp_post_template_data hook above.
*
* use `wp_footer` in Transition and Standard mode
* use `amp_post_template_footer` in Reader mode
*/
add_action( 'wp_footer', [ $this, 'add_auto_ads_amp_body_script' ] );
add_action( 'amp_post_template_footer', [ $this, 'add_auto_ads_amp_body_script' ] );
// Other AMP plugins.
}
}
/**
* Add AdSense AMP Auto ads code to the header.
*
* @param array $data AMP components.
*/
public function add_auto_ads_amp_head_script( $data ) {
$data['amp_component_scripts']['amp-auto-ads'] = 'https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js';
return $data;
}
/**
* Add Adsense Auto Ads body script.
*/
public function add_auto_ads_amp_body_script() {
$pub_id = $this->data->get_adsense_id();
if ( $pub_id ) {
printf( '<amp-auto-ads type="adsense" data-ad-client="ca-%s"></amp-auto-ads>', esc_attr( $pub_id ) );
}
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* Output auto ads enabled code in head
*
* @var bool $privacy_enabled Whether to wait for user consent.
* @var bool $npa_enabled Whether to show non-personalized ads.
* @var string $client_id The Google AdSense client ID.
* @var bool $top_anchor AdSense anchor ad on top of pages.
* @var string $top_anchor_code The code for top anchor ads.
* @var string $script_src AdSense script url.
* @var bool $add_publisher_id Whether to add the publisher ID to the AdSense JavaScript URL.
*/
if ( $privacy_enabled ) : ?>
<script>
(function () {
var scriptDone = false;
document.addEventListener('advanced_ads_privacy', function (event) {
if (
(event.detail.state !== 'accepted' && event.detail.state !== 'not_needed' && !advads.privacy.is_adsense_npa_enabled())
|| scriptDone
) {
return;
}
// google adsense script can only be added once.
scriptDone = true;
var script = document.createElement('script'),
first = document.getElementsByTagName('script')[0];
script.async = true;
script.crossOrigin = 'anonymous';
script.src = '<?php echo esc_url( $script_src ); ?>';
<?php
if ( $top_anchor ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- relevant user input has already been escaped.
echo $top_anchor_code;
} elseif ( ! $add_publisher_id ) {
printf( 'script.dataset.adClient = "%s";', esc_attr( $client_id ) );
}
?>
first.parentNode.insertBefore(script, first);
});
})();
</script>
<?php
return;
endif;
// Privacy not enabled.
// phpcs:disable WordPress.WP.EnqueuedResources
if ( $top_anchor ) {
printf(
'<script async src="%s"></script><script>%s</script>',
esc_attr( $script_src ),
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- the snippet has already been escaped.
$top_anchor_code
);
} else {
// Don't add the data-ad-client attribute when the publisher ID is appended to the script URL.
printf(
'<script %s async src="%s" crossorigin="anonymous"></script>',
! $add_publisher_id ? 'data-ad-client="' . esc_attr( $client_id ) . '"' : '',
esc_url( $script_src )
);
}
// phpcs:enable

View File

@@ -0,0 +1,536 @@
(() => {
window.wp.domReady(() => {
init(window.wp);
});
function init(wp) {
/**
* Shortcut variables
*/
const el = wp.element.createElement,
registerBlockType = wp.blocks.registerBlockType,
RawHTML = wp.element.RawHTML,
aaGut = window.advadsGutenberg,
i18n = aaGut.i18n,
textFlow = aaGut.textFlow,
safeHTML = wp.dom.safeHTML,
editor = wp.blockEditor,
comp = wp.components;
/**
* Custom SVG icon
* could move to a separated file if we need it in other places, too
*
* <source> https://gist.github.com/zgordon/e837e29f77c343d29ebb7290a1a75eea
*/
const advadsIconEl = el(
'svg',
{
width: '24px',
height: '24px',
viewBox: '1.396 3276 24 24',
xmlns: 'http://www.w3.org/2000/svg',
x: '0px',
y: '0px',
},
el(
'g',
{},
el('path', {
fill: '#1C1B3A',
d: 'M18.602,3286.2v8.53H6.677v-11.925h8.53c-0.355-0.804-0.545-1.684-0.545-2.625s0.205-1.82,0.545-2.625 h-2.57H1.406v18.266l0.6,0.6l-0.6-0.6c0,2.304,1.875,4.179,4.18,4.179l0,0h7.05h11.216v-13.821 c-0.805,0.355-1.705,0.566-2.645,0.566C20.286,3286.745,19.406,3286.541,18.602,3286.2z',
}),
el('circle', {
fill: '#0E75A4',
cx: '21.206',
cy: '3280.179',
r: '4.18',
})
)
);
/**
* Register the single ad block type
*/
registerBlockType('advads/gblock', {
apiVersion: 2,
title: i18n.advads,
icon: advadsIconEl,
category: 'common',
attributes: {
className: {
type: 'string',
default: '',
},
itemID: {
type: 'string',
default: '',
},
width: {
type: 'string',
default: '',
},
height: {
type: 'string',
default: '',
},
align: {
type: 'string',
default: 'default',
},
},
// todo: make the keywords translatable
keywords: ['advert', 'adsense', 'banner'],
edit: (props) => {
const itemID = props.attributes.itemID;
/**
* Update itemID
*
* @param {Event} event change event on the select input.
*/
function setID(event) {
props.setAttributes({
itemID: event.target.querySelector('option:checked')
.value,
});
}
/**
* Update width
*
* @param {Event} event change event on the number input.
*/
function setWidth(event) {
props.setAttributes({ width: event.target.value });
}
/**
* Update height
*
* @param {Event} event change event on the number input.
*/
function setHeight(event) {
props.setAttributes({ height: event.target.value });
}
/**
* Request hints related to the item.
*
* @param {string} ID Item ID.
*/
function requestHints(ID) {
if (!ID || 0 !== ID.indexOf('group_')) {
setHints([]);
return;
}
const data = new FormData();
data.append('action', 'advads-get-block-hints');
data.append('nonce', window.advadsglobal.ajax_nonce);
data.append('itemID', itemID);
fetch(window.ajaxurl, {
method: 'POST',
credentials: 'same-origin',
body: data,
})
.then((response) => response.json())
.then((json) => {
if (json.success) {
setHints(json.data);
}
})
.catch((error) => {
// eslint-disable-next-line no-console -- Might help experienced users
console.info(error);
});
}
function createSizeInputs(label, name, onchange) {
const randomID =
'advanced-ads-size' +
(Math.random() + 1).toString(36).substring(1);
return el(
'div',
{ className: 'size-group' },
el(
'label',
{ htmlFor: randomID },
el('span', { className: 'head' }, label)
),
el(
'div',
{ className: 'size-input' },
el('input', {
type: 'number',
inputMode: 'numeric',
id: randomID,
value: props.attributes[name],
name,
min: 0,
max: Infinity,
step: 1,
onChange: onchange,
}),
el('span', { className: 'suffix' }, 'px')
)
);
}
const [hints, setHints] = window.wp.element.useState([]);
window.wp.element.useEffect(() => {
requestHints(itemID);
}, [itemID]);
// the form children elements
const children = [];
// argument list (in array form) for the children creation
const args = [],
ads = [],
groups = [],
placements = [];
args.push('select');
args.push({
value: props.attributes.itemID,
onChange: setID,
key: 'advads-select-item',
});
args.push(el('option', { key: 'empty' }, i18n['--empty--']));
for (const adID in aaGut.ads) {
if (typeof aaGut.ads[adID].id === 'undefined') {
continue;
}
ads.push(
el(
'option',
{
value: 'ad_' + aaGut.ads[adID].id,
key: adID,
},
aaGut.ads[adID].title
)
);
}
for (const GID in aaGut.groups) {
if ('undefined' === typeof aaGut.groups[GID].id) {
continue;
}
groups.push(
el(
'option',
{
value: 'group_' + aaGut.groups[GID].id,
key: GID,
},
aaGut.groups[GID].name
)
);
}
if (aaGut.placements) {
for (const pid in aaGut.placements) {
if ('undefined' === typeof aaGut.placements[pid].id) {
continue;
}
placements.push(
el(
'option',
{
value: 'place_' + aaGut.placements[pid].id,
key: pid,
},
aaGut.placements[pid].name
)
);
}
}
if (aaGut.placements) {
args.push(
el(
'optgroup',
{
label: i18n.placements,
key: 'placements',
},
placements
)
);
}
args.push(
el(
'optgroup',
{
label: i18n.adGroups,
key: 'adGroups',
},
groups
)
);
args.push(el('optgroup', { label: i18n.ads, key: 'ads' }, ads));
// add a <label /> first and style it.
children.push(
el(
'div',
{
className: 'components-placeholder__label',
key: 'advads-block-title',
},
advadsIconEl,
el(
'label',
{ style: { display: 'block' } },
i18n.advads
)
)
);
if (itemID && i18n['--empty--'] !== itemID) {
let url = '#';
if (0 === itemID.indexOf('place_')) {
url = aaGut.editLinks.placement;
} else if (0 === itemID.indexOf('group_')) {
url = aaGut.editLinks.group;
} else if (0 === itemID.indexOf('ad_')) {
url = aaGut.editLinks.ad.replace(
'%ID%',
itemID.substr(3)
);
}
children.push(
el(
'div',
{
className: 'components-placeholder__fieldset',
key: 'advads-select-wrap',
},
// then add the <select /> input with its own children
el.apply(null, args),
el('a', {
className: 'dashicons dashicons-external',
style: {
margin: 5,
},
href: url,
target: '_blank',
key: 'advads-item-link',
})
)
);
hints.forEach(function (item, index) {
children.push(
el(
RawHTML,
{
key: index,
className:
'advads-block-hint advads-notice-inline advads-error',
},
safeHTML(item)
)
);
});
} else {
children.push(el.apply(null, args));
}
if (!aaGut.ads.length) {
children.push(
el(
'div',
{
className: 'components-placeholder__label',
key: 'advads-first-ad',
},
'',
el(
'a',
{
href: window.advadsglobal.create_ad_url,
className: 'button',
target: '_blank',
style: {
display: 'block',
marginTop: '10px',
},
},
window.advadsglobal.create_your_first_ad
)
)
);
}
const sizePanel = el(
'div',
{ id: 'advanced-ads-size-wrap' },
el(
'div',
null,
createSizeInputs(i18n.width, 'width', setWidth),
createSizeInputs(i18n.height, 'height', setHeight)
)
);
const sidebar = el(
editor.InspectorControls,
{ key: 'advads-sidebar' },
el(
comp.PanelBody,
{
title: i18n.size,
initialOpen: true,
},
sizePanel
)
);
children.push(sidebar);
const alignmentItems = [];
for (const slug in textFlow) {
const isSelected = props.attributes.align === slug;
alignmentItems.push(
el(
comp.MenuItem,
{
key: slug,
label: textFlow[slug].label,
onClick: () =>
props.setAttributes({ align: slug }),
isSelected,
},
el(
'div',
{
className:
'text-flow-wrap' +
(isSelected ? ' current' : ''),
},
el(
'div',
{
className: 'text-flow-icon',
},
el('img', {
src: `${aaGut.imagesUrl}${slug}.png`,
alt: slug,
title: textFlow[slug].label,
className: 'standard',
}),
el('img', {
src: `${aaGut.imagesUrl}${slug}-alt.png`,
alt: slug,
title: textFlow[slug].label,
className: 'alternate',
})
),
el(
'div',
{
className: 'text-flow-label',
title: textFlow[slug].description,
},
el('span', {}, textFlow[slug].label)
)
)
)
);
}
const toolBar = el(
editor.BlockControls,
{
key: 'advads-toolbar',
group: 'block',
},
el(
comp.ToolbarGroup,
{
title: 'Alignment',
},
el(comp.ToolbarDropdownMenu, {
icon: 'editor-alignleft',
label: 'Choose an alignment',
children: () =>
el(
'div',
{ className: 'advads-align-dropdown' },
alignmentItems
),
})
)
);
// return the complete form
return el(
'div',
editor.useBlockProps(),
el(
'form',
{
className: 'components-placeholder is-large',
},
children
),
toolBar
);
},
save: () => {
// server side rendering
return null;
},
// Transforms legacy widget to Advanced Ads block.
transforms: {
from: [
{
type: 'block',
blocks: ['core/legacy-widget'],
isMatch: (attributes) => {
if (
!attributes.instance ||
!attributes.instance.raw
) {
// Can't transform if raw instance is not shown in REST API.
return false;
}
return attributes.idBase === 'advads_ad_widget';
},
transform: (attributes) => {
const instance = attributes.instance.raw;
const transformedBlock = wp.blocks.createBlock(
'advads/gblock',
{
name: instance.name,
itemID: instance.item_id,
}
);
if (!instance.title) {
return transformedBlock;
}
return [
wp.blocks.createBlock('core/heading', {
content: instance.title,
}),
transformedBlock,
];
},
},
],
},
});
}
})();

View File

@@ -0,0 +1,101 @@
#advanced-ads-size-wrap > div {
display: grid;
grid-template-columns: 50% 50%;
gap: 0.6em;
}
#advanced-ads-size-wrap input[type="number"] {
max-width: calc(100% - 1px);
padding: 0.2em 0.6em;
border: none;
outline: none;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
}
#advanced-ads-size-wrap input[type="number"]::-webkit-inner-spin-button,
#advanced-ads-size-wrap input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
}
#advanced-ads-size-wrap .size-input {
position: relative;
border: 1px solid #949494;
font-size: 14px;
line-height: 14px;
}
#advanced-ads-size-wrap .suffix {
position: absolute;
z-index: 9;
top: 0;
padding: 9px;
right: 2px;
}
.rtl #advanced-ads-size-wrap .suffix {
right: auto;
left: 2px;
}
#advanced-ads-size-wrap .head {
text-transform: uppercase;
font-weight: 500;
font-size: 11px;
}
.text-flow-wrap {
width: 100%;
display: grid;
grid-template-columns: 30% 60%;
gap: 0.5em;
}
.text-flow-wrap::after {
content: "";
display: block;
}
.text-flow-wrap img {
width: 36px;
height: 36px;
float: left;
}
.text-flow-wrap.current img {
width: 48px;
height: 48px;
}
.text-flow-wrap img.standard {
display: inline-block;
}
.text-flow-wrap img.alternate {
display: none;
}
.text-flow-wrap.current img.standard,
.text-flow-wrap:hover img.standard {
display: none;
}
.text-flow-wrap.current img.alternate,
.text-flow-wrap:hover img.alternate {
display: inline-block;
}
.advads-align-dropdown .components-button:focus:not(:disabled) {
box-shadow: none;
}
.advads-align-dropdown .text-flow-label {
display: flex;
align-content: center;
flex-wrap: wrap;
}
.text-flow-wrap.current .text-flow-label {
font-weight: 600;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,12 @@
<?php
// module configuration
$path = dirname( __FILE__ );
return [
'classmap' => [
'Advanced_Ads_Gutenberg' => $path . '/includes/class-gutenberg.php',
],
'textdomain' => null,
];

View File

@@ -0,0 +1,280 @@
<?php
// phpcs:ignoreFile
/**
* Gutenberg block registration
*
* @package Advanced_Ads
*/
/**
* Class Advanced_Ads_Gutenberg
*/
class Advanced_Ads_Gutenberg {
/**
* The singleton
*
* @var Advanced_Ads_Gutenberg
*/
private static $instance;
/**
* CSS classes to use on the frontend
*
* @var string
*/
private static $css_class;
/**
* Constructor
*/
private function __construct() {
add_action( 'init', [ $this, 'init' ] );
add_action( 'enqueue_block_editor_assets', [ $this, 'register_scripts' ] );
}
/**
* Register blocks
*/
public function init() {
if ( ! function_exists( 'register_block_type' ) ) {
// no Gutenberg, Abort.
return;
}
register_block_type(
'advads/gblock',
[
'editor_script_handles' => [ ADVADS_PLUGIN_BASENAME . '/gutenberg-ad' ],
'editor_style_handles' => [ ADVADS_PLUGIN_BASENAME . '/gutenberg-ad' ],
'render_callback' => [ $this, 'render_ad_selector' ],
]
);
/**
* Removes legacy widget from legacy widget block.
*
* @param string[] $widget_types An array of excluded widget-type IDs.
*
* @return array
*/
add_filter(
'widget_types_to_hide_from_legacy_widget_block',
function( $widget_types ) {
$widget_types[] = 'advads_ad_widget';
return $widget_types;
}
);
}
/**
* Register back end scripts
*
* @return void
*/
public function register_scripts() {
if ( ! function_exists( 'register_block_type' ) ) {
// no Gutenberg, Abort.
return;
}
wp_register_script(
ADVADS_PLUGIN_BASENAME . '/gutenberg-ad',
ADVADS_BASE_URL . 'modules/gutenberg/assets/advanced-ads.block.js',
[ 'wp-dom-ready', 'wp-blocks', 'wp-element' ],
ADVADS_VERSION,
false
);
$all_ads = wp_advads_ad_query(
[
'post_status' => [ 'publish' ],
'orderby' => 'title',
'order' => 'ASC',
]
)->posts;
$all_groups = wp_advads_get_all_groups();
$ads = [];
$groups = [];
$placements = [];
foreach ( $all_ads as $ad ) {
$ads[] = [
'id' => $ad->ID,
'title' => $ad->post_title,
];
}
foreach ( $all_groups as $group ) {
$groups[] = [
'id' => $group->get_id(),
'name' => $group->get_name(),
];
}
foreach ( wp_advads_get_all_placements() as $placement ) {
if ( $placement->is_type( [ 'sidebar_widget', 'default' ] ) ) {
$placements[] = [
'id' => $placement->get_id(),
'name' => $placement->get_title(),
];
}
}
ksort( $placements );
if ( empty( $placements ) ) {
$placements = false;
}
$i18n = [
'--empty--' => __( '--empty--', 'advanced-ads' ),
'advads' => __( 'Advanced Ads', 'advanced-ads' ),
'ads' => __( 'Ads', 'advanced-ads' ),
'adGroups' => __( 'Ad Groups', 'advanced-ads' ),
'placements' => __( 'Placements', 'advanced-ads' ),
'width' => __( 'Width', 'advanced-ads' ),
'height' => __( 'Height', 'advanced-ads' ),
'size' => __( 'Size', 'advanced-ads' ),
'alignment' => __( 'Alignment', 'advanced-ads' ),
];
$inline_script = wp_json_encode(
[
'ads' => $ads,
'groups' => $groups,
'placements' => $placements,
'editLinks' => [
'group' => admin_url( 'admin.php?page=advanced-ads-groups' ),
'placement' => admin_url( 'admin.php?page=advanced-ads-placements' ),
'ad' => admin_url( 'post.php?post=%ID%&action=edit' ),
],
'imagesUrl' => ADVADS_BASE_URL . 'modules/gutenberg/assets/img/',
'i18n' => $i18n,
'textFlow' => [
'default' => [
'label' => __( "Theme's default", 'advanced-ads' ),
'description' => __( 'The ad will behave as predefined by the theme.', 'advanced-ads' ),
],
'float-left' => [
'label' => __( "Float left", 'advanced-ads' ),
'description' => __( 'Text will wrap around the ad and its margin.', 'advanced-ads' ),
],
'float-right' => [
'label' => __( "Float right", 'advanced-ads' ),
'description' => __( 'Text will wrap around the ad and its margin.', 'advanced-ads' ),
],
'block-left' => [
'label' => __( "Block left", 'advanced-ads' ),
'description' => __( 'Text will continue after the ad and its margin.', 'advanced-ads' ),
],
'block-right' => [
'label' => __( "Block right", 'advanced-ads' ),
'description' => __( 'Text will continue after the ad and its margin.', 'advanced-ads' ),
],
'center' => [
'label' => __( "Centered", 'advanced-ads' ),
'description' => __( 'Text will continue after the ad and its margin.', 'advanced-ads' ),
],
],
]
);
// put the inline code with the global variable right before the block's JS file.
wp_add_inline_script( ADVADS_PLUGIN_BASENAME . '/gutenberg-ad', 'var advadsGutenberg = ' . $inline_script, 'before' );
wp_enqueue_script( ADVADS_PLUGIN_BASENAME . '/gutenberg-ad' );
wp_enqueue_style(
ADVADS_PLUGIN_BASENAME . '/gutenberg-ad',
ADVADS_BASE_URL . 'modules/gutenberg/assets/block.css',
[],
ADVADS_VERSION
);
}
/**
* Server side rendering for single ad block
*
* @param array $attr Block's attributes.
*/
public static function render_ad_selector( $attr ) {
ob_start();
if ( ! isset( $attr['itemID'] ) ) {
ob_end_clean();
return '';
}
$output = [
'output' => [
'class' => ! empty( $attr['className'] ) ? array_filter( explode( ' ', $attr['className'] ) ) : [],
],
];
if ( isset( $attr['fixed_widget'] ) ) {
$output['wrapper_attrs']['data-fixed_widget'] = esc_attr( $attr['fixed_widget'] );
}
if ( ! empty( $attr['width'] ) ) {
$output['output']['wrapper_attrs']['style']['width'] = absint( $attr['width'] ) . 'px';
}
if ( ! empty( $attr['height'] ) ) {
$output['output']['wrapper_attrs']['style']['height'] = absint( $attr['height'] ) . 'px';
}
$align = esc_attr( $attr['align'] ?? 'default' );
$after_ad_filter = function( $output ) {
return $output . '<br style="clear: both; display: block; float: none;">';
};
if ( 0 === strpos( $align, 'block' ) ) {
add_filter( 'advanced-ads-ad-output', $after_ad_filter );
}
switch ( $align ) {
case 'float-left':
case 'block-left':
$output['output']['wrapper_attrs']['style']['float'] = 'left';
break;
case 'float-right':
case 'block-right':
$output['output']['wrapper_attrs']['style']['float'] = 'right';
break;
case 'center':
$output['output']['wrapper_attrs']['style']['margin-left'] = 'auto';
$output['output']['wrapper_attrs']['style']['margin-right'] = 'auto';
$output['output']['wrapper_attrs']['style']['text-align'] = 'center';
break;
default:
}
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- we can't escape ad output without potentially breaking ads
if ( 0 === strpos( $attr['itemID'], 'ad_' ) ) {
echo get_the_ad( absint( substr( $attr['itemID'], 3 ) ), '', $output );
} elseif ( 0 === strpos( $attr['itemID'], 'group_' ) ) {
echo get_the_group( substr( $attr['itemID'], 6 ), '', $output );
} elseif ( 0 === strpos( $attr['itemID'], 'place_' ) ) {
$id = substr( $attr['itemID'], 6 );
echo get_the_placement( is_numeric( $id ) ? (int) $id : $id, '', $output );
}
// phpcs:enable
return ob_get_clean();
}
/**
* Return the unique instance
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
}

View File

@@ -0,0 +1,5 @@
<?php
if ( class_exists( 'Advanced_Ads', false ) ) {
Advanced_Ads_Gutenberg::get_instance();
}

View File

@@ -0,0 +1,191 @@
<?php
/**
* The class is responsible for adding widget in the WordPress admin area.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick\Admin;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Modules\OneClick\Helpers;
use AdvancedAds\Modules\OneClick\Options;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin.
*/
class Admin implements Integration_Interface {
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
add_action( 'advanced-ads-overview-widgets-after', [ $this, 'add_metabox' ] );
}
/**
* Enqueue scripts
*
* @param string $hook current page hook.
*
* @return void
*/
public function enqueue( $hook ): void {
if ( 'toplevel_page_advanced-ads' !== $hook ) {
return;
}
$config = Options::pubguru_config();
wp_advads()->registry->enqueue_script( 'oneclick-onboarding' );
wp_advads()->json->add(
'oneclick',
[
'security' => wp_create_nonce( 'pubguru_oneclick_security' ),
'isConnected' => false !== Options::pubguru_config(),
'btnCancel' => __( 'Cancel', 'advanced-ads' ),
'btnClose' => __( 'Close', 'advanced-ads' ),
'btnContinue' => __( 'Continue', 'advanced-ads' ),
'btnRetry' => __( 'Retry', 'advanced-ads' ),
'siteDomain' => WordPress::get_site_domain(),
'spinner' => ADVADS_BASE_URL . 'admin/assets/img/loader.gif',
'addonRow' => [
'icon' => ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-m2pg.svg',
'title' => __( 'MonetizeMore & PubGuru Integration', 'advanced-ads' ),
'content' => __( 'Enables MonetizeMore users to link their settings with the PubGuru insights & analytics dashboard.', 'advanced-ads' ),
'connect' => __( 'Connect now', 'advanced-ads' ),
'disconnect' => __( 'Disconnect now', 'advanced-ads' ),
],
'metabox' => [
'title' => __( 'MonetizeMore & PubGuru Integration', 'advanced-ads' ),
'visitText' => __( 'Visit the MonetizeMore website to learn about PubGuru', 'advanced-ads' ),
'visitLink' => 'https://www.monetizemore.com/solutions/pubguru',
],
'step1' => [
'title' => __( 'Onboarding Step 1 of 3, Consent and Privacy Policy', 'advanced-ads' ),
'content' => sprintf(
wp_kses_post(
/* translators: %s link to privacy policy */
__( 'This form is designed exclusively for MonetizeMore customers who wish to integrate Advanced Ads with their PubGuru Dashboard. In alignment with our <a href="%s">Privacy Policy</a>, no information other than your domain name is exchanged, and Advanced Ads does not engage in any tracking activities.', 'advanced-ads' )
),
'https://wpadvancedads.com/privacy-policy/'
),
'agreeText' => __( 'I agree to share my domain name to facilitate the connection with my PubGuru account.', 'advanced-ads' ),
'btnAgree' => __( 'Connect with PubGuru', 'advanced-ads' ),
],
'step2' => [
'title' => __( 'Onboarding Step 2 of 3, Connecting to PubGuru', 'advanced-ads' ),
'loading' => __( 'Fetching your domain information from PubGuru, please wait...', 'advanced-ads' ),
'notRegistered' => __( 'The domain &ldquo;{0}&rdquo; is not registered with PubGuru', 'advanced-ads' ),
'content' => sprintf(
/* translators: %1$s is contact link, %2$s is email link */
__( 'If you are on a domain &ldquo;unknow&rdquo; to PubGuru, e.g., a staging site, please indicate the domain that you registered with PubGuru. If you need assistance, please <a href="%1$s">click here to contact PubGuru support</a> or <a href="%2$s">send an email to support@monetizemore.com</a>.', 'advanced-ads' ),
'https://www.monetizemore.com/contact/',
'mailto:support@monetizemore.com'
),
'inputLabel' => __( 'Registered domain', 'advanced-ads' ),
'serverError' => __( 'The onboarding process has encountered an error: &ldquo;{0}&rdquo;', 'advanced-ads' ),
'serverContent' => sprintf(
/* translators: %1$s is contact link, %2$s is email link */
__( 'Please wait a few minutes and try again. If you need assistance, please <a href="%1$s">click here to contact PubGuru support</a> or <a href="%2$s">send an email to support@monetizemore.com</a>.', 'advanced-ads' ),
'https://www.monetizemore.com/contact/',
'mailto:support@monetizemore.com'
),
],
'step3' => [
'title' => __( 'Onboarding Step 3 of 3, Test and Finalize Ad Unit Import', 'advanced-ads' ),
'yourDomain' => __( 'Your domain &ldquo;{0}&rdquo; is connected to PubGuru.', 'advanced-ads' ),
'btnImport' => __( 'Import PubGuru Ad Units', 'advanced-ads' ),
'importContent' => wp_kses_post(
join(
'',
[
'<p>' . __( 'This step is entirely optional. Your PubGuru configuration shows the following available ad units', 'advanced-ads' ) . '</p>',
'<ul class="list-disc ml-4">',
'<li>' . __( '3 In-Content Ads', 'advanced-ads' ) . '</li>',
'<li>' . __( '1 Leaderboard Ad', 'advanced-ads' ) . '</li>',
'</ul>',
'<p>' . __( 'You will be able to preview the ad units injections on a test page before and there is a rollback option after the import.', 'advanced-ads' ) . '</p>',
]
)
),
'previewContent' => __( 'You may preview or change the test page for the ad units&rsquo; injections, or finalize the PubGuru Ad Unit import.', 'advanced-ads' ),
'finalContent' => sprintf(
/* translators: %s rollback page link */
__( 'You have successfully imported your PubGuru Ad Units. If necessary, use the <a href="%s">Rollback Tool</a> to revert your ad setup to a previous state.', 'advanced-ads' ),
esc_url( admin_url( 'admin.php?page=advanced-ads-tools#import-history' ) )
),
],
'settings' => [
'title' => __( 'General Settings', 'advanced-ads' ),
'help' => sprintf(
/* translators: %1$s is contact link, %2$s is email link */
__( 'If you need assistance, please <a href="%1$s">click here to contact PubGuru support</a> or <a href="%2$s">send an email to support@monetizemore.com</a>.', 'advanced-ads' ),
'https://www.monetizemore.com/contact/',
'mailto:support@monetizemore.com'
),
'headerBidding' => __( 'Activate PubGuru Header Bidding', 'advanced-ads' ),
'activateTags' => __( 'Activate Tag Conversion', 'advanced-ads' ),
'trafficCop' => __( 'Activate Traffic Cop Invalid Traffic Protection', 'advanced-ads' ),
'trafficCopTrial' => __( '7 Days Trial', 'advanced-ads' ),
'adsTxt' => sprintf(
/* translators: %s is link to PubGuru */
__( 'Redirect ads.txt calls to the <a href="%s" target="_blank" rel="noreferrer">PubGuru platform</a>', 'advanced-ads' ),
'https://app.pubguru.com/ads-txt'
),
'scriptLocation' => __( 'Move the PubGuru Header Bidding script to the footer. <span class="muted">Keep this option disabled to maximize revenue. Only enable it if PageSpeed is your priority.</span>', 'advanced-ads' ),
'onlyPreview' => __( '(Only enabled on Preview Page)', 'advanced-ads' ),
],
'options' => [
'headerBidding' => Options::module( 'header_bidding' ),
'headerBiddingAtBody' => Options::module( 'header_bidding_at_body' ),
'adsTxt' => Options::module( 'ads_txt' ),
'trafficCop' => Options::module( 'traffic_cop' ),
'tagConversion' => Options::module( 'tag_conversion' ),
'connectedDomain' => $config['domain'] ?? '',
'selectedMethod' => $config['method'] ?? 'page',
'selectedPage' => $config['page'] ?? 0,
'selectedPageTitle' => isset( $config['page'] ) ? get_the_title( $config['page'] ) : '',
'hasTrafficCop' => Helpers::has_traffic_cop( $config ),
],
'modal' => [
'title' => __( 'Import PubGuru Ad Units', 'advanced-ads' ),
'btnSave' => __( 'Close and update preview', 'advanced-ads' ),
'btnFinal' => __( 'Finalize ad unit import', 'advanced-ads' ),
'btnUpdate' => __( 'Update preview', 'advanced-ads' ),
'btnGoto' => __( 'Go to the preview', 'advanced-ads' ),
'labelImport' => __( 'Import method', 'advanced-ads' ),
'labelSpecificPage' => __( 'Preview ad units on specific page', 'advanced-ads' ),
'labelFinalImport' => __( 'Finalize ad units import', 'advanced-ads' ),
'descFinalImport' => join(
'',
[
'<ul class="list-disc ml-2">',
'<li>' . __( 'Your existing ads and placements will be set to &lsquo;Draft&rsquo; mode', 'advanced-ads' ) . '</li>',
'<li>' . __( 'Your PubGuru ad units will be imported as suitable ads and placements, and published right away', 'advanced-ads' ) . '</li>',
'</ul>',
'<p>' . __( 'You can manually republish specific ads and placements or fully rollback at any time.', 'advanced-ads' ) . '</p>',
]
),
],
]
);
}
/**
* Add metabox
*
* @return void
*/
public function add_metabox(): void {
echo '<div id="advads-oneclick-app"></div>';
}
}

View File

@@ -0,0 +1,261 @@
<?php
/**
* The class is responsible for ajax functionality.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick\Admin;
use WP_Error;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Modules\OneClick\Helpers;
use AdvancedAds\Modules\OneClick\Options;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Modules\OneClick\AdsTxt\Detector;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
use AdvancedAds\Importers\Api_Ads;
defined( 'ABSPATH' ) || exit;
/**
* Ajax.
*/
class Ajax implements Integration_Interface {
/**
* API URL
*
* Production: https://app.pubguru.com
* Development: https://new-stagingtools5.pubguru.com
*
* @var string
*/
const API_URL = 'https://app.pubguru.com/';
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_action( 'init', [ $this, 'init' ] );
add_action( 'wp_ajax_search_posts', [ $this, 'search_posts' ] );
add_action( 'wp_ajax_pubguru_connect', [ $this, 'pubguru_connect' ] );
add_action( 'wp_ajax_pubguru_disconnect', [ $this, 'pubguru_disconnect' ] );
add_action( 'wp_ajax_pubguru_module_change', [ $this, 'module_status_changed' ] );
add_action( 'wp_ajax_pubguru_backup_ads_txt', [ $this, 'backup_ads_txt' ] );
add_action( 'wp_ajax_update_oneclick_preview', [ $this, 'update_oneclick_preview' ] );
}
/**
* Init hook
*
* @return void
*/
public function init(): void {
if ( Params::get( 'refresh_ads', false, FILTER_VALIDATE_BOOLEAN ) ) {
$config = $this->pubguru_api_connect();
if ( is_wp_error( $config ) ) {
wp_die(
$config->get_error_message(), // phpcs:ignore
esc_html__( 'Refreshing PubGuru Ads', 'advanced-ads' ),
$config->get_error_data() // phpcs:ignore
);
}
}
}
/**
* PubGuru Connect
*
* @return void
*/
public function pubguru_connect(): void {
check_ajax_referer( 'pubguru_oneclick_security', 'nonce' );
$config = $this->pubguru_api_connect();
if ( is_wp_error( $config ) ) {
wp_send_json(
[
'success' => false,
'code' => $config->get_error_code(),
'message' => $config->get_error_message(),
],
$config->get_error_data()
);
}
// Default module enabled.
Options::module( 'header_bidding', true );
wp_send_json_success(
[
'message' => esc_html__( 'We have successfully migrated your MonetizeMore PubGuru Ad Units to your WordPress site. The existing placements and ads have been paused.', 'advanced-ads' ),
'hasTrafficCop' => Helpers::has_traffic_cop( $config ),
]
);
}
/**
* PubGuru Disconnect
*
* @return void
*/
public function pubguru_disconnect(): void {
check_ajax_referer( 'pubguru_oneclick_security', 'nonce' );
Options::pubguru_config( 'delete' );
wp_send_json_success(
[
'message' => esc_html__( 'PubGuru successfully disconnected.', 'advanced-ads' ),
]
);
}
/**
* Handle module status changes
*
* @return void
*/
public function module_status_changed(): void {
check_ajax_referer( 'pubguru_oneclick_security', 'security' );
$module = Params::post( 'module', [] );
$status = Params::post( 'status', false, FILTER_VALIDATE_BOOLEAN );
Options::module( $module, $status );
$data = apply_filters( 'pubguru_module_status_changed', [], $module, $status );
wp_send_json_success( $data );
}
/**
* Handle module status changes
*
* @return void
*/
public function backup_ads_txt(): void {
check_ajax_referer( 'pubguru_oneclick_security', 'security' );
$result = ( new Detector() )->backup_file();
if ( false === $result ) {
$notice = sprintf(
'<div class="flex items-center">'
/* translators: 1 is the opening link to the Advanced Ads website, 2 the closing link */
. __( 'The backup of your ads.txt file has failed. Please ensure that a manual backup is created You can find detailed instructions on how to manually back up your ads.txt file in the manual. %1$sManual%2$s', 'advanced-ads' ) // phpcs:ignore
. '</div>',
'<a href="https://wpadvancedads.com/manual/ads-txt/?utm_source=advanced-ads&utm_medium=link&utm_campaign=notice-ads-txt-oci#Manual_backup_of_the_adstxt_file" target="_blank" class="button button-link ml-auto mr-2">',
'</a>'
);
wp_send_json_error( $notice );
}
$notice = sprintf(
'<div class="flex items-center">%s</div>',
esc_html__( 'File successfully backed up.', 'advanced-ads' )
);
wp_send_json_success( $notice );
}
/**
* Search posts
*
* @return void
*/
public function search_posts(): void {
global $wpdb;
check_ajax_referer( 'pubguru_oneclick_security', 'security' );
$search = Params::get( 'q', '' );
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID, post_title FROM {$wpdb->posts}
WHERE post_title LIKE %s
AND post_status = 'publish'
AND post_type IN ('post', 'page')",
'%' . $wpdb->esc_like( $search ) . '%'
)
);
$posts = [];
foreach ( $results as $result ) {
$posts[] = [
'id' => $result->ID,
'text' => $result->post_title,
];
}
wp_send_json( $posts );
}
/**
* Update OneClick preview
*
* @return void
*/
public function update_oneclick_preview(): void {
check_ajax_referer( 'pubguru_oneclick_security', 'security' );
$method = Params::post( 'method', 'page' );
$page = Params::post( 'page', 0, FILTER_VALIDATE_INT );
$config = Options::pubguru_config();
$config['method'] = $method;
$config['page'] = $page;
Options::pubguru_config( $config );
// Importer.
$importer = new Api_Ads();
$importer->import();
wp_send_json_success();
}
/**
* Fetch config from PubGuru api
*
* @return WP_Error|array
*/
private function pubguru_api_connect() {
$domain = Params::post( 'testDomain' ) ? Params::post( 'testDomain' ) : WordPress::get_site_domain();
$domain = str_replace( 'www.', '', $domain );
$response = wp_remote_get(
self::API_URL . 'domain_configs/?domain=' . $domain,
[
'timeout' => 30,
'sslverify' => false,
]
);
$response_code = wp_remote_retrieve_response_code( $response );
if ( 200 !== $response_code ) {
return new WP_Error(
'connect_error',
esc_html__( 'An error has occurred please try again.', 'advanced-ads' ),
$response_code
);
}
$config = json_decode( wp_remote_retrieve_body( $response ), true );
if ( 'error' === $config['status'] ) {
return new WP_Error(
'domain_not_found',
'Connection with PubGuru & MonetizeMore was unsuccessful. Please <a href="https://www.monetizemore.com/contact/">click here</a> to contact MonetizeMore Support or email us at <a href="mailto:support@monetizemore.com">support@monetizemore.com</a>',
201
);
}
$config['domain'] = $domain;
Options::pubguru_config( $config );
return $config;
}
}

View File

@@ -0,0 +1,4 @@
<?php
/**
* Silence is golden.
*/

View File

@@ -0,0 +1,220 @@
<?php
/**
* Helpers.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Utilities\Str;
defined( 'ABSPATH' ) || exit;
/**
* Helpers.
*/
class Helpers {
/**
* Check if config has traffic cop subscription
*
* @param array $config Config instance.
*
* @return bool
*/
public static function has_traffic_cop( $config = null ): bool {
if ( null === $config ) {
$config = Options::pubguru_config();
}
if (
isset( $config['params'] ) &&
( isset( $config['params']['trafficCopIvtAction'] ) && 'block' === $config['params']['trafficCopIvtAction'] ) &&
( isset( $config['params']['trafficCopTestPercent'] ) && $config['params']['trafficCopTestPercent'] > 0.01 )
) {
return true;
}
return false;
}
/**
* Get config file
*
* Cases
* 1. For auto_created
* 2. For mobile prefix
* 3. For desktop prefix
* 4. If none of the prefix is found than go with the first one
* 5. No configuration found => pg.{domain}.js
*
* @return bool|string
*/
public static function get_config_file() {
static $pubguru_config_name;
if ( null !== $pubguru_config_name ) {
return $pubguru_config_name;
}
$pubguru_config_name = false;
$configs = Options::pubguru_config();
// 1. For auto_created => pg.{domain}_auto_created.js
foreach ( $configs['configs'] as $config ) {
if ( isset( $config['auto_created'] ) && $config['auto_created'] ) {
$pubguru_config_name = $config['name'];
return $pubguru_config_name;
}
}
// 5. No configuration found => pg.{domain}.js
if ( ! isset( $configs['configs'] ) || empty( $configs['configs'] ) ) {
$domain = WordPress::get_site_domain( 'name' );
return "pg.{$domain}.js";
}
$pubguru_config_name = wp_is_mobile()
// 2. For mobile prefix
? self::config_contains( 'mobile' )
// 3. For desktop prefix
: self::config_contains( 'desktop' );
$pubguru_config_name = false !== $pubguru_config_name
? $pubguru_config_name
// 4. If none of the prefix is found than go with the first one
: $configs['configs'][0]['name'];
return $pubguru_config_name;
}
/**
* Find config name by needle
*
* @param string $needle Needle to look into config name.
*
* @return bool|string
*/
private static function config_contains( $needle ) {
$configs = Options::pubguru_config();
foreach ( $configs['configs'] as $config ) {
if ( Str::contains( $needle, $config['name'] ) ) {
return $config['name'];
}
}
return false;
}
/**
* Get ads from saved config.
*
* In this order
* 1. Get ads from auto_created = true
* 2. Get ads from first config
* 3. Get ads from default config
*
* @return bool|array
*/
public static function get_ads_from_config() {
static $pubguru_config_ads;
$config = Options::pubguru_config();
if ( null !== $pubguru_config_ads ) {
return $pubguru_config_ads;
}
$pubguru_config_ads = false;
// 1. Get ads from auto_created = true or name contains auto_created
foreach ( $config['configs'] as $config ) {
if (
( isset( $config['auto_created'] ) && $config['auto_created'] ) ||
Str::contains( 'auto_created', $config['name'] )
) {
$pubguru_config_ads = $config['ad_units'];
return $pubguru_config_ads;
}
}
// 2. Get ads from first config
if ( isset( $config['configs'][0]['ad_units'] ) && ! empty( $config['configs'][0]['ad_units'] ) ) {
$pubguru_config_ads = $config['configs'][0]['ad_units'];
} else {
// 3. Get ads from default config
$domain = WordPress::get_site_domain();
$pubguru_config_ads = [
$domain . '_leaderboard' => [
'ad_unit' => $domain . '_leaderboard',
'slot' => $domain . '_leaderboard',
'device' => 'all',
'advanced_placement' => [
'placement' => 'beforeContent',
'inContentRule' => [
'position' => 'before',
'positionCount' => 1,
'positionElement' => 'paragraph',
'positionRepeat' => false,
],
],
],
$domain . '_in_content_1' => [
'ad_unit' => $domain . '_in_content_1',
'slot' => $domain . '_in_content_1',
'device' => 'all',
'advanced_placement' => [
'placement' => 'beforeContent',
'inContentRule' => [
'position' => 'before',
'positionCount' => 1,
'positionElement' => 'paragraph',
'positionRepeat' => false,
],
],
],
$domain . '_in_content_2' => [
'ad_unit' => $domain . '_in_content_2',
'slot' => $domain . '_in_content_2',
'device' => 'all',
'advanced_placement' => [
'placement' => 'inContent',
'inContentRule' => [
'position' => 'after',
'positionCount' => 3,
'positionElement' => 'paragraph',
'positionRepeat' => true,
],
],
],
];
}
return $pubguru_config_ads;
}
/**
* Is ad disabled on page
*
* @param int $post_id Post id to check for.
*
* @return bool
*/
public static function is_ad_disabled( $post_id = 0 ): bool {
global $post;
if ( ! $post ) {
return false;
}
if ( ! $post_id ) {
$post_id = $post->ID;
}
$settings = get_post_meta( $post_id, '_advads_ad_settings', true );
return is_singular() ? ! empty( $settings['disable_ads'] ) : false;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Options.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
defined( 'ABSPATH' ) || exit;
/**
* Options.
*/
class Options {
const CONFIG_KEY = '_advads_pubguru_connect_config';
/**
* Read and Write pubguru config
*
* @param array $data Array of pubguru configuration.
*
* @return bool|array
*/
public static function pubguru_config( $data = null ) {
if ( null === $data ) {
return get_option( self::CONFIG_KEY );
}
if ( 'delete' === $data ) {
return delete_option( self::CONFIG_KEY );
}
return update_option( self::CONFIG_KEY, $data );
}
/**
* Read and Write pubguru module status
*
* @param string $module Module name.
* @param bool $status Module status.
*
* @return bool
*/
public static function module( $module, $status = null ): bool {
$option_key = 'pubguru_module_' . $module;
if ( null === $status ) {
return boolval( get_option( $option_key, false ) );
}
update_option( $option_key, $status );
return $status;
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* Modules TrafficCop Page Parser.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Modules TrafficCop Page Parser.
*
* phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
*/
class Page_Parser implements Integration_Interface {
/**
* Hold page.
*
* @var string
*/
private $page = '';
/**
* Get Parser Instance
*
* @return Page_Parser
*/
public static function get_instance() {
static $instance;
if ( null === $instance ) {
$instance = new Page_Parser();
$instance->hooks();
}
return $instance;
}
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'template_redirect', [ $this, 'start_buffer' ], -9999 );
add_action( 'wp_footer', [ $this, 'flush_page' ], 9999 );
}
/**
* Get page
*
* @return string
*/
public function get_page() {
return $this->page;
}
/**
* Start of buffer.
*
* @return void
*/
public function start_buffer() {
ob_start( [ $this, 'parse' ] );
}
/**
* Parse page for script tag
*
* @param string $buffer Page buffer.
*
* @return string
*/
public function parse( $buffer ): string {
$this->page = $buffer;
$this->loop_script_tags();
$this->page = apply_filters( 'pubguru_current_page', $this->page ); // phpcs:ignore
return $this->page;
}
/**
* Flush page after footer
*
* @return void
*/
public function flush_page() {
$buffer_status = ob_get_status();
if (
! empty( $buffer_status ) &&
1 === $buffer_status['type'] &&
get_class( $this ) . '::parse' === $buffer_status['name']
) {
ob_end_flush();
}
}
/**
* Loop through script tags.
*
* @return void
*/
public function loop_script_tags() {
// Early bail!!
if ( ! has_filter( 'pubguru_page_script_tag' ) ) {
return;
}
$scripts = $this->get_script_tags();
foreach ( $scripts as $script ) {
$find = $script;
$replace = apply_filters( 'pubguru_page_script_tag', $script );
if ( false !== $replace ) {
$this->page = str_replace( $find, $replace, $this->page );
}
}
}
/**
* Get script tags only.
*
* @return array
*/
private function get_script_tags() {
$matches = [];
preg_match_all( '/<script[\s\S]*?>[\s\S]*?<\/script>/i', $this->page, $matches );
return isset( $matches[0] ) ? $matches[0] : [];
}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* OneClick Connect module
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
use AdvancedAds\Modules\OneClick\Admin\Admin;
use AdvancedAds\Modules\OneClick\Admin\Ajax;
use AdvancedAds\Modules\OneClick\Workflow;
// Common.
( new Workflow() )->hooks();
// Admin.
if ( is_admin() ) {
( new Ajax() )->hooks();
( new Admin() )->hooks();
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* The class is responsible to redirect ads.txt to centralized location.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick\AdsTxt;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Utilities\Str;
defined( 'ABSPATH' ) || exit;
/**
* AdsTxt.
*/
class AdsTxt implements Integration_Interface {
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
remove_action( 'advanced-ads-plugin-loaded', 'advanced_ads_ads_txt_init' );
add_action( 'template_redirect', [ $this, 'handle_redirect' ] );
add_filter( 'allowed_redirect_hosts', [ $this, 'allowed_redirect_hosts' ] );
}
/**
* Handle redirect
*
* @return void
*/
public function handle_redirect(): void {
if (
'ads-txt' !== get_query_var( 'name' ) ||
Str::contains( Params::server( 'REQUEST_URI' ), 'ads.txt' )
) {
return;
}
$redirect = sprintf( 'https://adstxt.pubguru.net/pg/%s/ads.txt', WordPress::get_site_domain() );
wp_safe_redirect( $redirect, 301 );
exit;
}
/**
* Allowed redirect hosts
*
* @param array $hosts Array to hold allowed hosts.
*
* @return array
*/
public function allowed_redirect_hosts( $hosts ): array {
$hosts[] = 'adstxt.pubguru.net';
return $hosts;
}
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* The class is responsible to detect ads.txt.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick\AdsTxt;
use AdvancedAds\Modules\OneClick\Admin\Admin;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Detector.
*/
class Detector implements Integration_Interface {
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_action( 'current_screen', [ $this, 'conditional_loading' ] );
}
/**
* Detect ads.txt physical file
*
* @return void
*/
public function conditional_loading(): void {
if ( ! Conditional::is_screen_advanced_ads() ) {
return;
}
if ( $this->detect_files() ) {
add_action( 'all_admin_notices', [ $this, 'show_notice' ] );
}
}
/**
* Detect file exists
*
* @param string $file file name.
*
* @return bool
*/
public function detect_files( $file = 'ads.txt' ): bool {
$wp_filesystem = $this->get_filesystem();
if ( null === $wp_filesystem ) {
return false;
}
return $wp_filesystem->exists( ABSPATH . '/' . $file );
}
/**
* Backup file
*
* @return bool
*/
public function backup_file(): bool {
$source = ABSPATH . '/ads.txt';
$destination = $source . '.bak';
return $this->move_file( $source, $destination );
}
/**
* Revert file
*
* @return bool
*/
public function revert_file(): bool {
$source = ABSPATH . '/ads.txt.bak';
$destination = ABSPATH . 'ads.txt';
return $this->move_file( $source, $destination );
}
/**
* Show notice that physical file exists
*
* @return void
*/
public function show_notice(): void {
?>
<div class="notice notice-error flex items-center p-4">
<div>
<strong><?php esc_html_e( 'File alert!', 'advanced-ads' ); ?></strong> <?php esc_html_e( 'Physical ads.txt found. In order to use PubGuru service you need to delete it or back it up.', 'advanced-ads' ); ?>
</div>
<button class="!ml-auto button button-primary js-btn-backup-adstxt" data-text="<?php esc_attr_e( 'Backup the File', 'advanced-ads' ); ?>" data-loading="<?php esc_attr_e( 'Backing Up', 'advanced-ads' ); ?>" data-done="<?php esc_attr_e( 'Backed Up', 'advanced-ads' ); ?>" data-security="<?php echo wp_create_nonce( 'pubguru_oneclick_security' ); // phpcs:ignore ?>">
<?php esc_html_e( 'Backup the File', 'advanced-ads' ); ?>
</button>
</div>
<?php
}
/**
* Instantiates the WordPress filesystem for use
*
* @return object
*/
private function get_filesystem() {
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
require_once ABSPATH . '/wp-admin/includes/file.php';
WP_Filesystem();
}
return $wp_filesystem;
}
/**
* Handle file operations (backup or revert)
*
* @param string $source Source file path.
* @param string $destination Destination file path.
*
* @return bool
*/
private function move_file( string $source, string $destination ): bool {
$wp_filesystem = $this->get_filesystem();
if ( null === $wp_filesystem || ! $wp_filesystem->is_writable( $source ) ) {
return false;
}
return $wp_filesystem->move( $source, $destination );
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* The class is responsible to inject header bidding tags.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
use AdvancedAds\Framework\Utilities\Str;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Header bidding tags.
*/
class Header_Bidding implements Integration_Interface {
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_filter( 'pubguru_page_script_tag', [ $this, 'remove_tags' ] );
add_filter( 'pubguru_current_page', [ $this, 'add_tags' ] );
}
/**
* Add TrafficCop tags
*
* @param string $page Page html.
*
* @return string
*/
public function add_tags( $page ): string {
$page = $this->add_script_tag( $page );
return $page;
}
/**
* Remove script tag
*
* @param string $script Scrip tag.
*
* @return string
*/
public function remove_tags( $script ): string {
if ( Str::contains( '/gpt.js', $script ) ) {
$script = '';
}
if ( Str::contains( '//m2d.m2.ai/', $script ) || Str::contains( '//c.pubguru.net/', $script ) ) {
$script = '';
}
if ( Str::contains( 'window.pg.atq = window.pg.atq || [];', $script ) ) {
$script = '';
}
return $script;
}
/**
* Guard the page from getting JS errors
*
* @param string $page Page html.
*
* @return string
*/
private function add_script_tag( $page ): string {
$name = Helpers::get_config_file();
$at_body = Options::module( 'header_bidding_at_body' );
if ( $name ) {
$script = [];
if ( ! $at_body ) {
$script[] = '<head>';
}
$script[] = '<script src="https://securepubads.g.doubleclick.net/tag/js/gpt.js" async></script>'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
$script[] = '<script type="text/javascript">window.googletag=window.googletag||{};window.googletag.cmd=window.googletag.cmd||[];';
$script[] = 'window.googletag.cmd.push(function(){window.__onpageGptEmbed=(new Date()).getTime()})</script>';
$script[] = sprintf( '<script src="//c.pubguru.net/%s" async> </script>', $name ); // phpcs:ignore
if ( Options::module( 'traffic_cop' ) && Helpers::has_traffic_cop() ) {
$script[] = '<script>window.pg = window.pg || {};window.pg.atq = window.pg.atq || [];</script>';
}
if ( $at_body ) {
$page = $page . join( "\n", $script );
} else {
$page = str_replace(
'<head>',
join( "\n", $script ),
$page
);
}
}
return $page;
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* The class is responsible to convert ad tags to pubguru tag.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
use AdvancedAds\Modules\OneClick\Helpers;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Ad tags conversion.
*/
class Tags_Conversion implements Integration_Interface {
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_filter( 'pubguru_current_page', [ $this, 'convert_tags' ], 20 );
}
/**
* Convert tags
*
* @param string $page Page html.
*
* @return string
*/
public function convert_tags( $page ): string {
$page = $this->convert_ad_by_config( $page );
return $page;
}
/**
* Convert ad divs to pubguru tags
*
* @param string $page Page html.
*
* @return string
*/
private function convert_ad_by_config( $page ): string {
$slots = $this->get_ad_slots();
if ( false === $slots ) {
return $page;
}
$divs = $this->get_ad_divs( $page, $slots );
foreach ( $divs as $div ) {
$replace = str_replace(
[ '<div', '</div>' ],
[ '<pubguru', '</pubguru>' ],
$div
);
$page = str_replace( $div, $replace, $page );
}
return $page;
}
/**
* Get ad divs only
*
* @param string $page Page html.
* @param array $slots Ad slots ids.
*
* @return array
*/
private function get_ad_divs( $page, $slots ): array {
$matches = [];
$slot_ids = [];
foreach ( $slots as $slot ) {
$slot_ids[] = preg_quote( $slot, '/' );
}
$slot_ids = join( '|', $slot_ids );
preg_match_all( '/<div id="(' . $slot_ids . ')"[^>]*>(.*?)<\/div>/mis', $page, $matches );
return isset( $matches[0] ) ? $matches[0] : [];
}
/**
* Get slots from config
*
* @return bool|array
*/
private function get_ad_slots() {
$ads = Helpers::get_ads_from_config();
if ( false === $ads ) {
return false;
}
$slots = [];
foreach ( $ads as $ad ) {
$slots[] = $ad['slot'];
}
return $slots;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* The class is responsible to wrap the google tags into traffic cop wrappers.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
use AdvancedAds\Framework\Utilities\Str;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Traffic Cop.
*/
class Traffic_Cop implements Integration_Interface {
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_filter( 'pubguru_page_script_tag', [ $this, 'modify_script_tag' ], 30 );
}
/**
* Modify scrip if google tag found
*
* @param mixed $content Scrip tag.
*
* @return bool|string
*/
public function modify_script_tag( $content ) {
// Early bail!!
if ( ! Str::contains( 'googletag.display', $content ) ) {
return false;
}
$content = str_replace( '<script>', '<script>pg.atq.push(function() {', $content );
$content = str_replace( '</script>', '});</script>', $content );
return $content;
}
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* The class is responsible for the one-click module workflow.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Modules\OneClick;
use AdvancedAds\Modules\OneClick\Helpers;
use AdvancedAds\Modules\OneClick\Options;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Modules\OneClick\Traffic_Cop;
use AdvancedAds\Modules\OneClick\AdsTxt\AdsTxt;
use AdvancedAds\Modules\OneClick\AdsTxt\Detector;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Workflow.
*/
class Workflow implements Integration_Interface {
/**
* Flush rules option key.
*
* @var string
*/
const FLUSH_KEY = 'pubguru_flush_rewrite_rules';
/**
* Hook into WordPress
*
* @return void
*/
public function hooks(): void {
add_action( 'init', [ $this, 'flush_rewrite_rules' ], 999 );
add_filter( 'pubguru_module_status_changed', [ $this, 'module_status_changed' ], 10, 3 );
if ( false !== Options::pubguru_config() && ! is_admin() ) {
add_action( 'wp', [ $this, 'init' ] );
if ( Options::module( 'ads_txt' ) ) {
( new AdsTxt() )->hooks();
}
if ( ! function_exists( 'wp_advads_pro' ) ) {
add_filter( 'advanced-ads-placement-content-offsets', [ $this, 'placement_content_offsets' ], 10, 3 );
}
}
if ( Options::module( 'ads_txt' ) ) {
if ( is_admin() ) {
( new Detector() )->hooks();
}
remove_action( 'advanced-ads-plugin-loaded', 'advanced_ads_ads_txt_init' );
}
}
/**
* Init workflow
*
* @return void
*/
public function init(): void {
// Early bail!!
$is_debugging = Params::get( 'aa-debug', false, FILTER_VALIDATE_BOOLEAN );
if ( ! $is_debugging && Helpers::is_ad_disabled() ) {
return;
}
Page_Parser::get_instance();
if ( $is_debugging || Options::module( 'header_bidding' ) ) {
$config = Options::pubguru_config();
$config['method'] = $config['method'] ?? 'page';
if ( 'page' === $config['method'] && isset( $config['page'] ) && is_page( absint( $config['page'] ) ) ) {
( new Header_Bidding() )->hooks();
}
if ( 'final' === $config['method'] ) {
( new Header_Bidding() )->hooks();
}
}
if ( $is_debugging || Options::module( 'tag_conversion' ) ) {
( new Tags_Conversion() )->hooks();
}
if ( Options::module( 'traffic_cop' ) && Helpers::has_traffic_cop() ) {
if ( ! Options::module( 'header_bidding' ) ) {
( new Header_Bidding() )->hooks();
}
( new Traffic_Cop() )->hooks();
}
}
/**
* Handle module status change
*
* @param array $data Data to send back to ajax request.
* @param string $module Module name.
* @param bool $status Module status.
*
* @return array
*/
public function module_status_changed( $data, $module, $status ): array {
if ( 'ads_txt' === $module ) {
$detector = new Detector();
if ( $status && $detector->detect_files() ) {
$data['notice'] = join(
'',
[
'<strong>' . esc_html__( 'File alert!', 'advanced-ads' ) . '</strong>',
' ',
esc_html__( 'Physical ads.txt found. In order to use PubGuru service you need to delete it or back it up.', 'advanced-ads' ),
]
);
$data['action'] = esc_html__( 'Backup the File', 'advanced-ads' );
}
if ( ! $status && $detector->detect_files( 'ads.txt.bak' ) ) {
$detector->revert_file();
}
update_option( self::FLUSH_KEY, 1 );
}
return $data;
}
/**
* Flush the rewrite rules once if the pubguru_flush_rewrite_rules option is set
*
* @return void
*/
public function flush_rewrite_rules(): void {
if ( get_option( self::FLUSH_KEY ) ) {
flush_rewrite_rules();
delete_option( self::FLUSH_KEY );
}
}
/**
* Get offsets for Content placement.
*
* @param array $offsets Existing Offsets.
* @param array $options Injection options.
* @param array $placement_opts Placement options.
*
* @return array $offsets New offsets.
*/
public function placement_content_offsets( $offsets, $options, $placement_opts ) {
if ( ! isset( $options['paragraph_count'] ) ) {
return $offsets;
}
// "Content" placement, repeat position.
if (
( ! empty( $placement_opts['repeat'] ) || ! empty( $options['repeat'] ) ) &&
isset( $options['paragraph_id'] ) &&
isset( $options['paragraph_select_from_bottom'] )
) {
$offsets = [];
for ( $i = $options['paragraph_id'] - 1; $i < $options['paragraph_count']; $i++ ) {
// Select every X number.
if ( 0 === ( $i + 1 ) % $options['paragraph_id'] ) {
$offsets[] = $options['paragraph_select_from_bottom'] ? $options['paragraph_count'] - 1 - $i : $i;
}
}
}
return $offsets;
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="140" overflow="visible"><path fill="#edfbff" d="M0 0h80v140H0z"/><g fill="#f2fcff"><path d="M0 0l40.066 23.312L80 0zm40.066 70L80 46.688v46.691z"/><path d="M0 93.379V46.688L40.066 70z"/></g><path fill="#e8faff" d="M80 0L40.066 23.312V70L80 46.688z"/><path fill="#f2fcff" d="M0 140l40.066-23.379L80 140z"/><path fill="#e8faff" d="M40.066 70L0 93.379V140l40.066-23.379z"/></svg>

After

Width:  |  Height:  |  Size: 435 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,206 @@
<?php
/**
* PEF module
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Modules\ProductExperimentationFramework;
/**
* Module main class
*/
class Module {
/**
* User meta key where the dismiss flag is stored.
*
* @var string
*/
const USER_META = 'advanced_ads_pef_dismiss';
/**
* Sum of all weights
*
* @var int
*/
private $weight_sum = 0;
/**
* ID => weight association
*
* @var int[]
*/
private $weights = [];
/**
* Whether the PEF can be displayed based on user meta
*
* @var bool
*/
private $can_display = true;
/**
* Return the singleton. Create it if needed
*
* @return Module
*/
public static function get_instance(): Module {
static $instance;
if ( null === $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Singleton design
*/
private function __construct() {
add_action( 'admin_init', [ $this, 'admin_init' ] );
}
/**
* Initialization
*
* @return void
*/
public function admin_init(): void {
$meta = get_user_meta( get_current_user_id(), self::USER_META, true );
if ( $meta && $this->get_minor_version( ADVADS_VERSION ) === $this->get_minor_version( $meta ) ) {
$this->can_display = false;
return;
}
$this->collect_weights();
add_action( 'wp_ajax_advanced_ads_pef', [ $this, 'dismiss' ] );
}
/**
* Ajax action to hie PEF for the current user until next plugin update
*
* @return void
*/
public function dismiss(): void {
if ( ! check_ajax_referer( 'advanced_ads_pef' ) ) {
wp_send_json_error( 'Unauthorized', 401 );
}
update_user_meta( get_current_user_id(), self::USER_META, ADVADS_VERSION );
wp_send_json_success( 'OK', 200 );
}
/**
* Get a random feature based on weights and a random number
*
* @return array
*/
public function get_winner_feature(): array {
$random_weight = wp_rand( 1, $this->weight_sum );
$current_weight = 0;
foreach ( $this->get_features() as $id => $feature ) {
$current_weight += $this->weights[ $id ];
if ( $random_weight <= $current_weight ) {
return array_merge(
[
'id' => $id,
'weight' => $this->weights[ $id ],
],
$this->get_features()[ $id ]
);
}
}
}
/**
* Render PEF
*
* @param string $screen the screen on which PEF is displayed, used in the utm_campaign parameter.
*
* @return void
*/
public function render( $screen ): void { // phpcs:ignore
// Early bail!!
if ( ! $this->can_display ) {
return;
}
$winner = $this->get_winner_feature();
require_once DIR . '/views/template.php';
}
/**
* Get minor part of a version
*
* @param string $version version to get the minor part from.
*
* @return string
*/
public function get_minor_version( $version ): string {
return explode( '.', $version )[1] ?? '0';
}
/**
* Build the link for the winner feature with all its utm parameters
*
* @param array $winner the winner feature.
* @param string $screen the screen on which it was displayed.
*
* @return string
*/
public function build_link( $winner, $screen ): string {
$utm_source = 'advanced-ads';
$utm_medium = 'link';
$utm_campaign = sprintf( '%s-aa-labs', $screen );
$utm_term = sprintf(
'b%s-w%d-%d',
str_replace( '.', '-', ADVADS_VERSION ),
$winner['weight'],
$this->weight_sum
);
$utm_content = $winner['id'];
return sprintf(
'https://wpadvancedads.com/advanced-ads-labs/?utm_source=%s&utm_medium=%s&utm_campaign=%s&utm_term=%s&utm_content=%s',
$utm_source,
$utm_medium,
$utm_campaign,
$utm_term,
$utm_content
);
}
/**
* Set the features/banners
*
* @return array
*/
public function get_features(): array {
return [
'labs-amazon-integration' => [
'subheading' => __( 'FROM THE ADVANCED ADS LABS:', 'advanced-ads' ),
'heading' => __( 'The Amazon Integration', 'advanced-ads' ),
'weight' => 1,
'text' => __( 'Our latest product concept puts Amazon affiliate marketing at your fingertips—right within Advanced Ads. It offers features like direct product import via Amazon API, multiple product display formats, and efficient ad tracking. We aim to create a one-stop solution for featuring Amazon products on your site without resorting to expensive third-party plugins.', 'advanced-ads' ),
'cta' => __( 'Are you interested in this product concept?', 'advanced-ads' ),
'cta_button' => __( 'Yes, I want to know more!', 'advanced-ads' ),
],
];
}
/**
* Collect feature ID with their weight as recorded in the class constant. Also calculate the weight sum
*/
private function collect_weights() {
if ( 0 !== $this->weight_sum ) {
return;
}
foreach ( $this->get_features() as $id => $feature ) {
$this->weights[ $id ] = (int) $feature['weight'];
$this->weight_sum += $this->weights[ $id ];
}
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* PEF module main file
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Modules\ProductExperimentationFramework;
const DIR = __DIR__;
const FILE = __FILE__;
if ( ! is_admin() ) {
return;
}
Module::get_instance();

View File

@@ -0,0 +1,138 @@
<?php
/**
* Final output for the Product Experimentation
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*
* @var array $winner the winner feature.
* @var string $screen the screen where it's displayed.
*/
?>
<style>
#support #advads_overview_pef {
max-width: 998px;
}
#advads_overview_pef {
border: 1px solid #0474a2;
border-radius: 5px;
margin: 22px 0;
color: #1b193a;
}
#advads_overview_pef div.aa_overview_pef_upper {
padding: 44px 44px 44px 208px;
background: url(<?php echo esc_url( trailingslashit( plugin_dir_url( \AdvancedAds\Modules\ProductExperimentationFramework\FILE ) ) ) . '/assets/aa-pef-bg.svg'; ?>) top left / 40px auto repeat;
border-radius: 5px 5px 0 0;
color: inherit;
}
#advads_overview_pef p.aa_overview_pef_dismiss {
position: absolute;
top: 22px;
right: 22px;
margin: 0;
text-align: right;
color: inherit;
}
#advads_overview_pef p.aa_overview_pef_dismiss a {
display: block;
text-decoration: none;
color: #1b193a;
}
#advads_overview_pef p.aa_overview_pef_subhead {
margin: 0 0 11px;
font-size: 18px;
line-height: 18px;
text-transform: uppercase;
font-weight: bold;
color: inherit;
}
#advads_overview_pef p.aa_overview_pef_subhead:before {
content: "";
position: absolute;
top: 44px;
left: 44px;
width: 120px;
height: 164px;
background: url(<?php echo esc_url( trailingslashit( plugin_dir_url( \AdvancedAds\Modules\ProductExperimentationFramework\FILE ) ) ) . '/assets/aa-pef-amazon-deco.svg'; ?>) center/contain no-repeat;
}
#advads_overview_pef h3.aa_overview_pef_head {
margin: 0 0 44px;
font-size: 36px;
line-height: 36px;
font-weight: bold;
color: inherit;
}
#advads_overview_pef p.aa_overview_pef_copy {
margin: 0 0 44px;
font-size: 18px;
line-height: 22px;
font-weight: normal;
color: inherit;
}
#advads_overview_pef p.aa_overview_pef_copy:last-child {
margin-bottom: 0;
}
#advads_overview_pef div.aa_overview_pef_lower {
padding: 22px 44px 22px 208px;
color: inherit;
}
#advads_overview_pef p.aa_overview_pef_cta {
margin: 0;
font-size: 18px;
font-weight: bold;
color: inherit;
}
#advads_overview_pef a.aa_overview_pef_button {
display: inline-block;
margin-left: 22px;
padding: 8px 22px;
font-size: 18px;
font-weight: bold;
border-radius: 5px;
color: #fff;
background: #1b193a;
text-decoration: none;
}
</style>
<script>
jQuery( document ).on( 'click', '.aa_overview_pef_dismiss', function ( ev ) {
ev.preventDefault();
wp.ajax.post(
'advanced_ads_pef',
{
_ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'advanced_ads_pef' ) ); ?>',
version: '<?php echo esc_js( ADVADS_VERSION ); ?>'
}
).done( function () {
jQuery( '#advads_overview_pef' ).remove();
} );
} );
</script>
<div id="advads_overview_pef" class="postbox position-full">
<div class="aa_overview_pef_upper">
<p class="aa_overview_pef_dismiss"><a class="dashicons dashicons-dismiss" href="#"></a></p>
<p class="aa_overview_pef_subhead"><?php echo esc_html( $winner['subheading'] ); ?></p>
<h3 class="aa_overview_pef_head"><?php echo esc_html( $winner['heading'] ); ?></h3>
<p class="aa_overview_pef_copy"><?php echo wp_kses_post( $winner['text'] ); ?></p>
</div>
<div class="aa_overview_pef_lower">
<p class="aa_overview_pef_cta">
<?php echo esc_html( $winner['cta'] ); ?>
<a class="aa_overview_pef_button" href="<?php echo esc_url( $this->build_link( $winner, $screen ) ); ?>" target="_blank">
<?php echo esc_html( $winner['cta_button'] ); ?>
</a>
</p>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More