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,175 @@
<?php
/**
* The class manages various admin action links, feedback submission and text overrides in footer.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin;
use WP_User;
use Advanced_Ads;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Action Links.
*/
class Action_Links implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'plugin_action_links_' . ADVADS_PLUGIN_BASENAME, [ $this, 'add_links' ] );
add_filter( 'admin_footer', [ $this, 'add_deactivation_popup' ] );
add_filter( 'admin_footer_text', [ $this, 'admin_footer_text' ], 100 );
add_action( 'wp_ajax_advads_send_feedback', [ $this, 'send_feedback' ] );
}
/**
* Add links to the plugins list
*
* @param array $links array of links for the plugins, adapted when the current plugin is found.
*
* @return array
*/
public function add_links( $links ): array {
// Early bail!!
if ( ! is_array( $links ) ) {
return $links;
}
// Add support page link.
$support_link = sprintf(
'<a href="%1$s">%2$s</a>',
esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#support' ) ),
__( 'Support', 'advanced-ads' )
);
// Add add-ons link.
$extend_link = defined( 'AAP_VERSION' )
? '<a href="https://wpadvancedads.com/add-ons/?utm_source=advanced-ads&utm_medium=link&utm_campaign=plugin-page-add-ons" target="_blank">' . __( 'Add-Ons', 'advanced-ads' ) . '</a>'
: '<a href="https://wpadvancedads.com/add-ons/all-access/?utm_source=advanced-ads&utm_medium=link&utm_campaign=plugin-page-features" target="_blank" class="aa-get-pro">' . __( 'See Pro Features', 'advanced-ads' ) . '</a>';
array_unshift( $links, $support_link, $extend_link );
return $links;
}
/**
* Display deactivation logic on plugins page
*
* @since 1.7.14
*
* @return void
*/
public function add_deactivation_popup(): void {
$screen = get_current_screen();
if ( ! isset( $screen->id ) || ! in_array( $screen->id, [ 'plugins', 'plugins-network' ], true ) ) {
return;
}
$from = '';
$email = '';
$current_user = wp_get_current_user();
if ( $current_user instanceof WP_User ) {
$from = sprintf( '%1$s <%2$s>', $current_user->user_nicename, trim( $current_user->user_email ) );
$email = $current_user->user_email;
}
include ADVADS_ABSPATH . 'views/admin/feedback-disable.php';
}
/**
* Overrides WordPress text in Footer
*
* @param string $text The footer text.
*
* @return string
*/
public function admin_footer_text( $text ): string {
if ( Conditional::is_screen_advanced_ads() ) {
return sprintf(
/* translators: %1$s is the URL to add a new review */
__( 'Thank the developer with a &#9733;&#9733;&#9733;&#9733;&#9733; review on <a href="%1$s" target="_blank">wordpress.org</a>', 'advanced-ads' ),
'https://wordpress.org/support/plugin/advanced-ads/reviews/#new-post'
);
}
return (string) $text;
}
/**
* Send feedback via email
*
* @since 1.7.14
*
* @return void
*/
public function send_feedback(): void {
$data = Params::post( 'formdata' );
if ( ! $data ) {
wp_die();
}
wp_parse_str( wp_unslash( $data ), $form );
if ( ! wp_verify_nonce( $form['advanced_ads_disable_form_nonce'], 'advanced_ads_disable_form' ) ) {
wp_die();
}
$email = trim( $form['advanced_ads_disable_reply_email'] );
if ( empty( $email ) || ! is_email( $email ) ) {
die();
}
$text = '';
$headers = [];
$options = Advanced_Ads::get_instance()->internal_options();
$installed = isset( $options['installed'] ) ? gmdate( 'd.m.Y', $options['installed'] ) : '';
$from = $form['advanced_ads_disable_from'] ?? '';
$subject = ( $form['advanced_ads_disable_reason'] ?? '(no reason given)' ) . ' (Advanced Ads)';
if ( isset( $form['advanced_ads_disable_text'] ) ) {
$text = implode( "\n\r", $form['advanced_ads_disable_text'] );
}
$text .= "\n\n" . home_url() . " ($installed)";
// The user clicked on the "dont disable" button or if an address is given in the form then use that one.
if (
isset( $form['advanced_ads_disable_reason'] ) &&
'get help' === $form['advanced_ads_disable_reason']
) {
$current_user = wp_get_current_user();
$name = ( $current_user instanceof WP_User ) ? $current_user->user_nicename : '';
$from = $name . ' <' . $email . '>';
$is_german = ( preg_match( '/\.de$/', $from ) || 'de_' === substr( get_locale(), 0, 3 ) || 'de_' === substr( get_user_locale(), 0, 3 ) );
if ( '' !== trim( $form['advanced_ads_disable_text'][0] ?? '' ) ) {
$text .= $is_german
? "\n\n Hilfe ist auf dem Weg."
: "\n\n Help is on its way.";
} else {
$text .= $is_german
? "\n\n Vielen Dank für das Feedback."
: "\n\n Thank you for your feedback.";
}
}
if ( $from ) {
$headers[] = "From: $from";
$headers[] = "Reply-To: $from";
}
wp_mail( 'improve@wpadvancedads.com', $subject, $text, $headers );
die();
}
}

View File

@@ -0,0 +1,509 @@
<?php
/**
* Admin Ad List Table.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
defined( 'ABSPATH' ) || exit;
use AdvancedAds\Options;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Abstracts\Admin_List_Table;
use AdvancedAds\Framework\Utilities\Params;
defined( 'ABSPATH' ) || exit;
/**
* Admin Ad List Table.
*/
class Ad_List_Table extends Admin_List_Table {
/**
* Object being shown on the row.
*
* @var Ad|null
*/
protected $object = null;
/**
* Object type.
*
* @var string
*/
protected $object_type = 'ad';
/**
* Post type.
*
* @var string
*/
protected $list_table_type = Constants::POST_TYPE_AD;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
parent::hooks();
add_filter( 'pre_get_posts', [ $this, 'posts_ordering' ] );
add_action( 'manage_posts_extra_tablenav', [ $this, 'display_views' ] );
add_filter( 'views_edit-' . $this->list_table_type, [ $this, 'add_views' ] );
}
/**
* Define which columns to show on this screen.
*
* @param array $columns Existing columns.
*
* @return array
*/
public function define_columns( $columns ): array {
// Remove the group taxonomy column as we have custom 'Used' column.
unset( $columns[ 'taxonomy-' . Constants::TAXONOMY_GROUP ] );
$new_columns = [];
foreach ( $columns as $key => $value ) {
$new_columns[ $key ] = $value;
if ( 'cb' === $key ) {
$new_columns['ad_type'] = __( 'Type', 'advanced-ads' );
continue;
}
if ( 'title' === $key ) {
$new_columns['title'] = __( 'Name', 'advanced-ads' );
$new_columns['ad_size'] = __( 'Size', 'advanced-ads' );
$new_columns['ad_timing'] = __( 'Ad Planning', 'advanced-ads' );
$new_columns['ad_shortcode'] = __( 'Ad Shortcode', 'advanced-ads' );
$new_columns['ad_adsense_id'] = __( 'AdSense ID', 'advanced-ads' );
$new_columns['ad_date'] = __( 'Date', 'advanced-ads' );
$new_columns['ad_description'] = __( 'Notes', 'advanced-ads' );
$new_columns['ad_preview'] = __( 'Preview', 'advanced-ads' );
$new_columns['ad_used'] = __( 'Used', 'advanced-ads' );
$new_columns['ad_debugmode'] = __( 'Debug Mode', 'advanced-ads' );
// show only when privacy setting is enabled.
if ( Options::instance()->get( 'privacy.enabled' ) ) {
$new_columns['ad_privacyignore'] = __( 'Privacy Ignore', 'advanced-ads' );
}
}
}
unset( $new_columns['date'] );
return apply_filters( 'advanced-ads-ad-columns', $new_columns );
}
/**
* Define which columns are sortable.
*
* @param array $columns Existing columns.
*
* @return array
*/
public function define_sortable_columns( $columns ): array {
$columns['ad_date'] = 'ad_date';
return $columns;
}
/**
* Define hidden columns.
*
* @return array
*/
protected function define_hidden_columns(): array {
$hidden[] = 'ad_description';
$hidden[] = 'author';
$hidden[] = 'ad_size';
$hidden[] = 'ad_shortcode';
$hidden[] = 'ad_date';
$hidden[] = 'ad_preview';
$hidden[] = 'ad_adsense_id';
$hidden[] = 'ad_debugmode';
$hidden[] = 'ad_privacyignore';
return $hidden;
}
/**
* Render any custom filters and search inputs for the list table.
*
* @return void
*/
protected function render_filters(): void {
$addate = Params::get( 'addate' );
if ( ! empty( $addate ) ) {
printf( '<input type="hidden" name="addate" value="%s">', esc_attr( $addate ) );
}
include ADVADS_ABSPATH . 'views/admin/tables/ads/filters.php';
}
/**
* Add expiring and expired ads view.
*
* @param array $views Available list table views.
*
* @return array
*/
public function add_views( $views ): array {
$counts = wp_count_posts( $this->list_table_type, 'readable' );
$expired = $counts->{Constants::AD_STATUS_EXPIRED};
$expiring = $counts->{Constants::AD_STATUS_EXPIRING};
if ( $expiring > 0 ) {
$views[ Constants::AD_STATUS_EXPIRING ] = sprintf(
'<a href="%s" %s>%s <span class="count">(%d)</span></a>',
add_query_arg(
[
'post_type' => Constants::POST_TYPE_AD,
'addate' => 'advads-filter-expiring',
'orderby' => 'expiry_date',
'order' => 'ASC',
],
'edit.php'
),
'advads-filter-expiring' === Params::request( 'addate' ) ? 'class="current" aria-current="page"' : '',
esc_attr_x( 'Expiring', 'Post list header for ads expiring in the future.', 'advanced-ads' ),
$expiring
);
}
if ( $expired > 0 ) {
$views[ Constants::AD_STATUS_EXPIRED ] = sprintf(
'<a href="%s" %s>%s <span class="count">(%d)</span></a>',
add_query_arg(
[
'post_type' => Constants::POST_TYPE_AD,
'addate' => 'advads-filter-expired',
'orderby' => 'expiry_date',
'order' => 'DESC',
],
'edit.php'
),
'advads-filter-expired' === Params::request( 'addate' ) ? 'class="current" aria-current="page"' : '',
esc_attr_x( 'Expired', 'Post list header for expired ads.', 'advanced-ads' ),
$expired
);
}
return $views;
}
/**
* Displays the list of views available for Ads.
*
* @param string $which The location of the extra table nav markup.
*
* @return void
*/
public function display_views( $which ): void {
global $wp_list_table;
if ( 'top' !== $which ) {
return;
}
$views = $wp_list_table->get_views();
/**
* Filters the list of available list table views.
*
* The dynamic portion of the hook name, `$this->screen->id`, refers
* to the ID of the current screen.
*
* @param string[] $views An array of available list table views.
*/
$views = apply_filters( "views_{$wp_list_table->screen->id}", $views );
if ( empty( $views ) ) {
return;
}
$wp_list_table->screen->render_screen_reader_content( 'heading_views' );
$is_all = count(
array_diff_key(
$_GET, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
[
'post_type' => Constants::POST_TYPE_AD,
'orderby' => '',
'order' => '',
'paged' => '',
'mode' => '',
]
)
) === 0;
$show_trash_delete_button = 'trash' === Params::get( 'post_status', false ) && have_posts() && current_user_can( get_post_type_object( $wp_list_table->screen->post_type )->cap->edit_others_posts );
include ADVADS_ABSPATH . 'views/admin/tables/ads/view-list.php';
}
/**
* Query filters.
*
* @param array $query_vars Query vars.
*
* @return array
*/
protected function query_filters( $query_vars ): array {
// Early bail!!
if ( wp_doing_ajax() ) {
return $query_vars;
}
if ( 'expiry_date' === $query_vars['orderby'] ) {
$query_vars['orderby'] = 'meta_value';
$query_vars['meta_key'] = Constants::AD_META_EXPIRATION_TIME; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
$query_vars['order'] = strtoupper( $query_vars['order'] ) === 'DESC' ? 'DESC' : 'ASC';
if ( 'advads-filter-expired' === Params::get( 'addate' ) ) {
$query_vars['post_status'] = Constants::AD_STATUS_EXPIRED;
}
}
$filter_author = Params::get( 'ad_author' );
if ( $filter_author ) {
$query_vars['author'] = $filter_author;
}
return $query_vars;
}
/**
* Modify the post listing order in the admin panel for a specific custom post type.
*
* @param WP_Query $query The WP_Query object.
*
* @return void
*/
public function posts_ordering( $query ): void {
global $typenow;
// Early bail!!
if ( ! $query->is_main_query() ) {
return;
}
if ( $this->list_table_type === $typenow ) {
$orderby = Params::get( 'orderby', 'title' );
$order = strtoupper( Params::get( 'order', 'ASC' ) );
if ( 'ad_date' === $orderby ) {
$orderby = 'post_modified';
}
$query->set( 'orderby', $orderby );
$query->set( 'order', $order );
}
}
/**
* Pre-fetch any data for the row each column has access to it.
*
* @param int $post_id Post ID being shown.
*
* @return void
*/
protected function prepare_row_data( $post_id ): void {
if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) {
$this->object = wp_advads_get_ad( $post_id );
}
}
/**
* Display the ad type icon in the ads list.
*
* @return void
*/
protected function render_ad_type_column(): void {
$ad = $this->object;
$size_string = $this->get_ad_size_string();
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-type.php';
}
/**
* Display the ad description in the ads list
*
* @return void
*/
protected function render_ad_description_column(): void {
$description = wp_trim_words( $this->object->get_description(), 50 );
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-description.php';
}
/**
* Display an ad preview in ads list.
*
* @return void
*/
protected function render_ad_preview_column(): void {
$type_object = $this->object->get_type_object();
// Early bail!!
if ( ! $type_object ) {
return;
}
if ( is_callable( [ $type_object, 'render_preview' ] ) ) {
$type_object->render_preview( $this->object );
}
do_action( 'advanced-ads-ad-list-details-column-after', $this->object, $type_object );
}
/**
* Display the ad size in the ads list
*
* @return void
*/
protected function render_ad_size_column(): void {
$size = $this->get_ad_size_string();
// Early bail!!
if ( empty( $size ) ) {
return;
}
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-size.php';
}
/**
* Display ad timing in ads list
*
* @return void
*/
protected function render_ad_timing_column(): void {
list(
'status_strings' => $status_strings,
'html_classes' => $html_classes,
) = $this->object->get_ad_schedule_details();
ob_start();
do_action_ref_array(
'advanced-ads-ad-list-timing-column-after',
[
$this->object,
&$html_classes,
]
);
$content_after = ob_get_clean();
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-timing.php';
}
/**
* Display ad shortcode in ads list
*
* @return void
*/
protected function render_ad_shortcode_column(): void {
$ad_id = $this->object->get_id();
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-shortcode.php';
}
/**
* Display an ad date in ads list.
*
* @return void
*/
protected function render_ad_date_column(): void {
$id = $this->object->get_id();
if ( ! $id ) {
return;
}
$datetime_regex = get_option( 'date_format' ) . ' \\a\\t ' . get_option( 'time_format' );
$published_date = get_the_date( $datetime_regex, $id );
$modified_date = get_the_modified_date( $datetime_regex, $id );
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-date.php';
}
/**
* Display the adsense id.
*
* @return void
*/
protected function render_ad_adsense_id_column(): void {
if ( null === $this->object->get_content() ) {
return;
}
$content = json_decode( $this->object->get_content() );
// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$slotid = $content->slotId ?? null;
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-adsense.php';
}
/**
* Display ad usage in groups & placements.
*
* @return void
*/
protected function render_ad_used_column(): void {
$ad_id = $this->object->get_id();
if ( ! $ad_id ) {
return;
}
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-used.php';
}
/**
* Display the debug mode in the ads list.
*
* @return void
*/
protected function render_ad_debugmode_column(): void {
$debug_mode = $this->object->is_debug_mode();
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-debug.php';
}
/**
* Display the privacy ignore status in the ads list.
*
* @return void
*/
protected function render_ad_privacyignore_column(): void {
$privacyignore = $this->object->get_prop( 'privacy.ignore-consent' ) ?? false;
include ADVADS_ABSPATH . 'views/admin/tables/ads/column-privacyignore.php';
}
/**
* Get the ad size string to display in post list.
*
* @return string
*/
private function get_ad_size_string(): string {
$size = '';
if ( ! empty( $this->object->get_width() ) || ! empty( $this->object->get_height() ) ) {
$size = sprintf( '%d &times; %d', $this->object->get_width(), $this->object->get_height() );
}
/**
* Filter the ad size string to display in the ads post list.
*
* @param string $size Size string.
* @param Ad $ad Ad instance.
*/
return (string) apply_filters( 'advanced-ads-list-ad-size', $size, $this->object );
}
}

View File

@@ -0,0 +1,749 @@
<?php
/**
* Admin Addon Box.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Arr;
defined( 'ABSPATH' ) || exit;
/**
* Admin Addon Box.
*/
class Addon_Box {
/**
* Hide active plugins
*
* @var bool
*/
private $hide_activated;
/**
* Internal plugins data
*
* @var array
*/
private $plugins;
/**
* Constructor
*
* @param bool $hide_activated whether to hide active plugins.
*/
public function __construct( $hide_activated = false ) {
if ( ! is_admin() ) {
return;
}
$this->hide_activated = (bool) $hide_activated;
$this->build_plugins_data();
}
/**
* Build the internal plugin data
*
* @return void
*/
private function build_plugins_data() {
// phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned,WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow
$aa_plugins = [
'advanced-ads-pro/advanced-ads-pro.php' => [
'id' => 'pro',
'constant' => 'AAP_VERSION',
'title' => 'Advanced Ads Pro',
'description' => __( 'Take the monetization of your website to the next level.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/advanced-ads-pro/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-responsive/responsive-ads.php' => [
'id' => 'ampads',
'constant' => 'AAR_VERSION',
'title' => 'AMP Ads',
'description' => __( 'Integrate your ads on AMP (Accelerated Mobile Pages) and auto-convert your Google AdSense ad units for enhanced mobile performance.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/responsive-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/ads-on-amp-pages/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-gam/advanced-ads-gam.php' => [
'id' => 'gam',
'constant' => 'AAGAM_VERSION',
'title' => 'Google Ad Manager Integration',
'description' => __( 'Simplify the process of implementing ad units from Google Ad Manager swiftly and without errors.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/google-ad-manager/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/google-ad-manager-integration-manual/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-layer/layer-ads.php' => [
'id' => 'popuplayer',
'constant' => 'AAPLDS_VERSION',
'title' => 'PopUp and Layer Ads',
'description' => __( 'Capture attention with customizable pop-ups that ensure your ads and messages get noticed. Set timing and closing options for optimal user engagement.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/popup-and-layer-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/popup-and-layer-ads-documentation/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-selling/advanced-ads-selling.php' => [
'id' => 'sellingads',
'constant' => 'AASA_VERSION',
'title' => 'Selling Ads',
'description' => __( 'Earn more money by enabling advertisers to buy ad space directly on your sites frontend.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/selling-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/selling-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-sticky-ads/sticky-ads.php' => [
'id' => 'stickyads',
'constant' => 'AASADS_SLUG',
'title' => 'Sticky Ads',
'description' => __( 'Increase click rates by anchoring ads in sticky positions above, alongside, or below your website.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/sticky-ads/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/sticky-ads-documentation/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-tracking/tracking.php' => [
'id' => 'tracking',
'constant' => 'AAT_VERSION',
'title' => 'Tracking',
'description' => __( 'Monitor your ad performance to maximize your revenue.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/tracking/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/tracking-documentation/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-slider/slider.php' => [
'id' => 'adslider',
'constant' => 'AAS_VERSION',
'title' => 'Ad Slider',
'description' => __( 'Create a beautiful ad slider and increase the ad impressions per page view. Free add-on for subscribers to our newsletter.', 'advanced-ads' ),
'download_link' => 'https://wpadvancedads.com/add-ons/slider/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons',
'manual' => 'https://wpadvancedads.com/manual/ad-slider/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual',
],
'advanced-ads-adsense-in-feed/advanced-ads-in-feed.php' => [
'id' => 'adsenseinfeed',
'constant' => 'AAINF_VERSION',
'title' => 'AdSense In-Feed',
'description' => __( 'Place AdSense In-feed ads between posts on homepage, category, and archive pages for optimal engagement.', 'advanced-ads' ),
'download_link' => wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=advanced-ads-adsense-in-feed' ), 'install-plugin_advanced-ads-adsense-in-feed' ),
'manual' => 'https://wpadvancedads.com/add-adsense-in-feed-to-wordpress/#Adding_the_In-feed_ad_to_your_WordPress_site',
],
];
// phpcs:enable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned,WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow
$plugins = get_plugins();
$this->plugins = [
'plugins' => [],
'premium_level' => 'free',
];
$pro = false;
$tracking = false;
$bundle_indicators = [
'ampads',
'stickyads',
'gam',
'sellingads',
'popuplayer',
];
foreach ( $aa_plugins as $file => $data ) {
if ( array_key_exists( $file, $plugins ) ) {
$this->plugins['plugins'][ $data['id'] ] = [ 'status' => defined( $data['constant'] ) ? 'active' : 'installed' ];
if ( in_array( $data['id'], $bundle_indicators, true ) ) {
$this->plugins['premium_level'] = 'bundle';
}
if ( in_array( $data['id'], [ 'pro', 'tracking' ], true ) ) {
${$data['id']} = true;
}
} else {
$this->plugins['plugins'][ $data['id'] ] = [ 'status' => 'missing' ];
}
$this->plugins['plugins'][ $data['id'] ]['file'] = $file;
if ( isset( $plugins[ $file ] ) ) {
$this->plugins['plugins'][ $data['id'] ]['name'] = $plugins[ $file ]['Name'];
}
$this->plugins['plugins'][ $data['id'] ] += $data;
}
if ( 'bundle' === $this->plugins['premium_level'] ) {
return;
}
if ( $pro || $tracking ) {
$this->plugins['premium_level'] = 'premium';
}
if ( $pro && $tracking ) {
$this->plugins['premium_level'] = 'bundle';
}
}
/**
* Get feature grid data for an addon
*
* @param string $id add-on internal ID.
*
* @return array
*/
private function get_grid_data( $id ) {
if ( 'pro' === $id ) {
return [
__( 'Cache Busting', 'advanced-ads' ),
__( 'Click Fraud Protection', 'advanced-ads' ),
__( 'Lazy Loading', 'advanced-ads' ),
__( 'Anti Ad Blocker', 'advanced-ads' ),
__( 'Geo Targeting', 'advanced-ads' ),
__( '+23 Conditions', 'advanced-ads' ),
__( '+11 Placements', 'advanced-ads' ),
__( 'Parallax Ads', 'advanced-ads' ),
__( 'Ad Grids', 'advanced-ads' ),
__( 'Ad Refresh', 'advanced-ads' ),
__( 'A/B Tests', 'advanced-ads' ),
];
}
if ( 'tracking' === $id ) {
return [
__( 'Impressions & Clicks', 'advanced-ads' ),
__( 'Click-Through Rate', 'advanced-ads' ),
__( 'Statistics', 'advanced-ads' ),
__( 'Google Analytics', 'advanced-ads' ),
__( 'Local Data Processing', 'advanced-ads' ),
__( 'Email Reports', 'advanced-ads' ),
__( 'Link Cloaking', 'advanced-ads' ),
];
}
return [];
}
/**
* Sort plugins by status (missing|installed|enabled)
*
* @param array $a plugin data.
* @param array $b plugin data.
*
* @return int
*/
private function sort_by_status( $a, $b ) {
static $order;
if ( null === $order ) {
$order = [
'missing' => 0,
'installed' => 1,
'active' => 2,
];
}
return $order[ $a['status'] ] < $order[ $b['status'] ] ? 1 : -1;
}
/**
* Sort extra add-ons by the order field
*
* @param int $a order to compare.
* @param int $b order to compare.
*
* @return mixed
*/
private function sort_by_order( $a, $b ) {
return (int) $a['order'] - (int) $b['order'];
}
/**
* Get plugin list to display into a given section
*
* @param string $section section slug (installed|available|special).
*
* @return array[]
*/
private function get_displayable_items( $section ) {
switch ( $section ) {
case 'special':
$items = [];
if ( 'missing' === $this->plugins['plugins']['adslider']['status'] ) {
$items['adslider'] = $this->plugins['plugins']['adslider'];
}
if ( 'missing' === $this->plugins['plugins']['adsenseinfeed']['status'] ) {
$items['adsenseinfeed'] = $this->plugins['plugins']['adsenseinfeed'];
}
return $items;
case 'available':
if ( 'free' === $this->plugins['premium_level'] ) {
return [
'aa' => [
'id' => 'aa',
'upsell' => true,
],
'aalt' => [ 'id' => 'aalt' ],
'pro' => array_merge( $this->plugins['plugins']['pro'], [ 'grid' => true ] ),
'tracking' => array_merge( $this->plugins['plugins']['tracking'], [ 'grid' => true ] ),
];
}
if ( 'premium' === $this->plugins['premium_level'] ) {
$items = [
'aa' => [
'id' => 'aa',
'upsell' => true,
],
'aalt' => [ 'id' => 'aalt' ],
];
if ( 'missing' === $this->plugins['plugins']['pro']['status'] ) {
$items['pro'] = $this->plugins['plugins']['pro'];
$items['pro']['grid'] = true;
}
if ( 'missing' === $this->plugins['plugins']['tracking']['status'] ) {
$items['tracking'] = $this->plugins['plugins']['tracking'];
$items['tracking']['grid'] = true;
}
return $items;
}
if ( 'bundle' === $this->plugins['premium_level'] ) {
return [ 'aalt' => [ 'id' => 'aalt' ] ];
}
return [];
default: // Installed add-ons.
if ( 'free' === $this->plugins['premium_level'] && 'missing' === $this->plugins['plugins']['adslider']['status'] && 'missing' === $this->plugins['plugins']['adsenseinfeed']['status'] ) {
return [
'none' =>
[
'id' => 'none',
],
];
}
if ( 'bundle' === $this->plugins['premium_level'] ) {
$items = [
'aa' => [ 'id' => 'aa' ],
]
+ Arr::where(
$this->get_special_add_ons(),
function ( $item ) {
return in_array( $item['status'], [ 'installed', 'active' ], true );
}
);
return $items;
}
$displayable_items = Arr::where(
$this->plugins['plugins'],
function ( $item ) {
return 'missing' !== $item['status'];
}
);
usort( $displayable_items, [ $this, 'sort_by_status' ] );
return $displayable_items;
}
}
/**
* Get list of plugins of the "special" section
*
* @return array
*/
private function get_special_add_ons() {
return Arr::where(
$this->plugins['plugins'],
function ( $item ) {
return in_array( $item['id'], [ 'adslider', 'adsenseinfeed' ], true );
}
);
}
/**
* Print output for a single add-on (not in bundle)
*
* @param array $item plugin data.
* @param string $section section slug.
*
* @return void
*/
private function do_single_item( $item, $section ) {
if ( 'aa' === $item['id'] ) {
$this->all_access( $item['upsell'] ?? false );
return;
}
if ( 'aalt' === $item['id'] ) {
$this->all_access_long_term();
return;
}
if ( 'none' === $item['id'] ) {
?>
<div class="single-item none">
<div class="item-details">
<div class="icon"><img src="<?php echo esc_url( ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-empty.svg' ); ?>" alt=""/></div>
<span></span>
<div class="name"><?php esc_html_e( 'No add-ons installed', 'advanced-ads' ); ?></div>
<span></span>
<div class="description">
<?php esc_html_e( 'Please select from the list below.', 'advanced-ads' ); ?>
<a href="https://wpadvancedads.com/manual/how-to-install-an-add-on/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-install-add-ons" target="_blank">
<?php esc_html_e( 'Learn how to download, install, and activate an add-on', 'advanced-ads' ); ?>
</a>
</div>
<span></span>
<div class="cta"></div>
</div>
</div>
<?php
return;
}
$button_class = $item['status'];
if ( $this->hide_activated && 'active' === $button_class ) {
return;
}
if ( 'active' === $item['status'] ) {
$button_class .= ' disabled';
}
if ( 'special' === $section && 'adslider' === $item['id'] ) {
$button_class = Conditional::user_can_subscribe( 'nl_free_addons' ) ? 'subscribe' : 'subscribed';
}
$grid_data = ! empty( $item['grid'] ) ? $this->get_grid_data( $item['id'] ) : false;
$button_target = 'available' === $section ? '_blank' : '_self';
?>
<div class="single-item">
<div class="item-details <?php echo esc_attr( $item['status'] ); ?>">
<div class="icon"><img src="<?php echo esc_url( ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-' . $item['id'] . '.svg' ); ?>" alt=""/></div>
<span></span>
<div class="name"><?php echo esc_html( $item['title'] ); ?></div>
<span></span>
<div class="description"><?php echo esc_html( $item['description'] ); ?></div>
<span></span>
<div class="cta">
<a href="<?php echo esc_url( $this->get_button_target( $item['id'], $section ) ); ?>"
<?php if ( 'subscribe' === $button_class ) : ?>
data-nonce="<?php echo esc_attr( wp_create_nonce( 'advads-newsletter-subscribe' ) ); ?>"
<?php endif; ?>
class="<?php echo esc_attr( "button $button_class" ); ?>" target="<?php echo esc_attr( $button_target ); ?>">
<i class="dashicons"></i>
<?php
switch ( $button_class ) {
case 'subscribe':
esc_html_e( 'Subscribe now', 'advanced-ads' );
break;
case 'subscribed':
esc_html_e( 'Download', 'advanced-ads' );
break;
case 'missing':
echo 'adsenseinfeed' === $item['id'] ? esc_html__( 'Install now', 'advanced-ads' ) : esc_html__( 'Upgrade', 'advanced-ads' );
break;
case 'installed':
esc_html_e( 'Activate now', 'advanced-ads' );
break;
default: // active disabled.
esc_html_e( 'Active', 'advanced-ads' );
}
?>
</a>
<?php if ( in_array( $section, [ 'installed', 'special' ], true ) ) : ?>
<div class="external-link">
<a href="<?php echo esc_url( $item['manual'] ); ?>" target="_blank">
<i class="dashicons dashicons-welcome-learn-more"></i>
<span><?php esc_html_e( 'See the manual', 'advanced-ads' ); ?></span>
</a>
</div>
<?php elseif ( 'available' === $section ) : ?>
<div class="external-link">
<a href="<?php echo esc_url( $item['download_link'] ); ?>" target="_blank">
<span><?php esc_html_e( 'Learn more', 'advanced-ads' ); ?></span>
</a>
</div>
<?php endif; ?>
</div>
</div>
<?php if ( $grid_data ) : ?>
<div class="item-grid">
<?php foreach ( $grid_data as $elem ) : ?>
<div class="feature">
<i class="dashicons"></i>
<span><?php echo esc_html( $elem ); ?></span>
</div>
<?php endforeach; ?>
<div class="feature more">
<i class="dashicons"></i>
<span><?php esc_html_e( 'many more features', 'advanced-ads' ); ?></span>
</div>
</div>
<?php endif; ?>
</div>
<?php
}
/**
* Get the href attribute of a call-to-action link
*
* @param string $id internal plugin ID.
* @param string $section section slug.
*
* @return mixed|string
*/
private function get_button_target( $id, $section = 'installed' ) {
if ( 'available' === $section ) {
$link = $this->plugins['plugins'][ $id ]['download_link'];
if ( 'tracking' !== $id ) {
$link = 'https://wpadvancedads.com/pricing/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons';
}
return $link;
}
if ( 'special' === $section && 'adslider' === $id ) {
return 'https://wpadvancedads.com/subscriber-resources/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons-manual';
}
if ( 'installed' === $section ) {
if ( 'installed' === $this->plugins['plugins'][ $id ]['status'] ) {
if ( version_compare( '6.5.0', get_bloginfo( 'version' ), '<=' ) ) {
return '#activate-aaplugin_' . wp_create_nonce( 'updates' ) . '_' . $this->plugins['plugins'][ $id ]['file'] . '_' . $this->plugins['plugins'][ $id ]['name'];
}
return wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . $this->plugins['plugins'][ $id ]['file'] . '&amp', 'activate-plugin_' . $this->plugins['plugins'][ $id ]['file'] );
}
return 'https://wpadvancedads.com/account/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons';
}
if ( 'special' === $section && 'adsenseinfeed' === $id && 'missing' === $this->plugins['plugins'][ $id ]['status'] ) {
return wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=advanced-ads-adsense-in-feed' ), 'install-plugin_advanced-ads-adsense-in-feed' );
}
return '#';
}
/**
* Print All Access Long Term output
*
* @return void
*/
private function all_access_long_term() {
?>
<div class="single-item">
<div class="item-details">
<div class="icon">
<img src="<?php echo esc_url( ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-allaccesslt.svg' ); ?>" alt=""/>
</div>
<span></span>
<div class="name">
<?php esc_html_e( 'Advanced Ads All Access long-term', 'advanced-ads' ); ?>
</div>
<span></span>
<div class="description">
<?php esc_html_e( 'Secure 4 years of ongoing support and updates with just one payment. Enjoy savings of up to 70% compared to individual add-on purchases.', 'advanced-ads' ); ?>
</div>
<span></span>
<div class="cta">
<div>
<a href="https://wpadvancedads.com/add-ons/all-access-long-term/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons" class="button upsell" target="_blank">
<i class="dashicons"></i>
<?php esc_html_e( 'Upgrade', 'advanced-ads' ); ?>
</a>
</div>
<div class="external-link">
<a href="https://wpadvancedads.com/add-ons/all-access-long-term/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons"><?php esc_html_e( 'Learn more', 'advanced-ads' ); ?></a>
</div>
</div>
</div>
</div>
<?php
}
/**
* Print All Access output
*
* @param bool $is_upsell whether it's an upsell or an installed (assumed) bundle.
*
* @return void
*/
private function all_access( $is_upsell = false ) {
$all_access_items = [
'pro',
'tracking',
'stickyads',
'popuplayer',
'ampads',
'gam',
'sellingads',
];
?>
<div class="bundle">
<div class="bundle-details">
<div class="icon">
<img src="<?php echo esc_url( ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-allaccess.svg' ); ?>" alt=""/>
</div>
<span></span>
<div class="name">
<?php esc_html_e( 'Advanced Ads All Access', 'advanced-ads' ); ?>
</div>
<span></span>
<div class="description">
<?php esc_html_e( 'Every tool you need for website success in one package. Enjoy our complete suite of add-ons for limitless possibilities.', 'advanced-ads' ); ?>
</div>
<span></span>
<div class="cta">
<?php if ( $is_upsell ) : ?>
<div>
<a href="https://wpadvancedads.com/pricing/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons" class="button upsell" target="_blank">
<i class="dashicons"></i><?php esc_html_e( 'Upgrade', 'advanced-ads' ); ?>
</a>
</div>
<div class="external-link">
<a href="https://wpadvancedads.com/add-ons/all-access/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-add-ons"><?php esc_html_e( 'Learn more', 'advanced-ads' ); ?></a>
</div>
<?php endif; ?>
</div>
</div>
<div class="bundle-items">
<?php
foreach ( $all_access_items as $item ) {
$this->do_bundle_item( $item, $is_upsell );
}
?>
</div>
</div>
<?php
}
/**
* Print output of a bundle's item
*
* @param string $id internal plugin ID.
* @param bool $is_upsell whether it's for an upsell. False if a bundle is assumed existing.
*
* @return void
*/
private function do_bundle_item( $id, $is_upsell = false ) {
$button_class = $this->plugins['plugins'][ $id ]['status'];
if ( 'active' === $this->plugins['plugins'][ $id ]['status'] ) {
$button_class .= ' disabled';
}
if ( $is_upsell ) {
$button_class = 'disabled';
}
$plugin = $this->plugins['plugins'][ $id ];
?>
<div class="bundle-item">
<div class="icon"><img src="<?php echo esc_url( ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-' . $id . '.svg' ); ?>" alt=""/></div>
<span></span>
<div class="name"><?php echo esc_html( $plugin['title'] ); ?></div>
<span></span>
<div class="description">
<?php echo esc_html( $plugin['description'] ); ?>
<a href="<?php echo esc_url( $plugin['download_link'] ); ?>" target="_blank"><?php esc_html_e( 'Learn more', 'advanced-ads' ); ?></a>
</div>
<span></span>
<div class="cta">
<div>
<a href="<?php echo $is_upsell ? '#' : esc_url( $this->get_button_target( $id ) ); ?>"
class="<?php echo esc_attr( "button $button_class" ); ?>"
target="<?php echo 'installed' === $plugin['status'] ? '_self' : '_blank'; ?>">
<?php if ( ! $is_upsell ) : ?>
<i class="dashicons"></i>
<?php endif; ?>
<?php
if ( $is_upsell ) {
esc_html_e( 'Included', 'advanced-ads' );
} elseif ( 'active' === $plugin['status'] ) {
esc_html_e( 'Active', 'advanced-ads' );
} elseif ( 'installed' === $plugin['status'] ) {
esc_html_e( 'Activate now', 'advanced-ads' );
} else {
esc_html_e( 'Download', 'advanced-ads' );
}
?>
</a>
</div>
</div>
</div>
<div class="separator"></div>
<?php
}
/**
* Displays the add-on box content
*
* @param bool $is_dashboard whether it's displayed on the dashboard screen.
*
* @return void
*/
public function display( $is_dashboard = true ) {
?>
<div id="advanced-ads-addon-box">
<span class="subheader"><?php esc_html_e( 'Installed Add-ons', 'advanced-ads' ); ?></span>
<?php foreach ( $this->get_displayable_items( 'installed' ) as $item ) : ?>
<?php $this->do_single_item( $item, 'installed' ); ?>
<?php endforeach; ?>
<span class="subheader"><?php esc_html_e( 'Available Add-ons', 'advanced-ads' ); ?></span>
<?php foreach ( $this->get_displayable_items( 'available' ) as $item ) : ?>
<?php $this->do_single_item( $item, 'available' ); ?>
<?php endforeach; ?>
<span class="subheader"><?php esc_html_e( 'Free Add-ons & Special Purpose', 'advanced-ads' ); ?></span>
<?php foreach ( $this->get_displayable_items( 'special' ) as $item ) : ?>
<?php $this->do_single_item( $item, 'special' ); ?>
<?php endforeach; ?>
<?php
$add_ons = apply_filters( 'advanced-ads-overview-add-ons', [] );
uasort( $add_ons, [ $this, 'sort_by_order' ] );
?>
<?php foreach ( $add_ons as $add_on ) : ?>
<?php
if ( ! $is_dashboard && empty( $add_on['outside_dashboard'] ) ) {
continue;
}
?>
<div class="single-item add-on <?php echo ! empty( $add_on['class'] ) ? esc_attr( $add_on['class'] ) : ''; ?>">
<div class="item-details">
<div class="icon">
<img src="<?php echo esc_url( ! empty( $add_on['icon'] ) ? $add_on['icon'] : ADVADS_BASE_URL . 'assets/img/add-ons/aa-addons-icons-empty.svg' ); ?>" alt=""/>
</div>
<span></span>
<div class="name"><?php echo esc_html( $add_on['title'] ); ?></div>
<span></span>
<div class="description"><?php echo esc_html( $add_on['desc'] ); ?></div>
<span></span>
<div class="cta <?php echo ! empty( $add_on['link'] ) && ! empty( $add_on['link_primary'] ) && $add_on['link_primary'] ? 'primary' : 'secondary'; ?>">
<?php if ( ! empty( $add_on['link'] ) ) : ?>
<a href="<?php echo esc_url( $add_on['link'] ); ?>" class="button">
<?php if ( ! empty( $add_on['link_icon'] ) ) : ?>
<i class="dashicons <?php echo esc_attr( $add_on['link_icon'] ); ?>"></i>
<?php endif; ?>
<?php echo ! empty( $add_on['link_title'] ) ? esc_html( $add_on['link_title'] ) : esc_html__( 'Get this add-on', 'advanced-ads' ); ?>
</a>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<footer>
<a href="https://wpadvancedads.com/manual/how-to-install-an-add-on/?utm_source=advanced-ads&utm_medium=link&utm_campaign=overview-install-add-ons" target="_blank">
<?php esc_html_e( 'How to download, install, and activate an add-on', 'advanced-ads' ); ?>
<span class="screen-reader-text"> (opens in a new tab)</span>
<span aria-hidden="true" class="dashicons dashicons-external"></span>
</a>
</footer>
<?php
}
}

View File

@@ -0,0 +1,182 @@
<?php
/**
* Admin Addon Updater.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Data;
use Advanced_Ads_Admin_Licenses;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Addon Updater.
*/
class Addon_Updater implements Integration_Interface {
/**
* Get the license manager.
*
* @var \Advanced_Ads_Admin_Licenses
*/
private $manager = null;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
$this->manager = Advanced_Ads_Admin_Licenses::get_instance();
if ( ! wp_doing_ajax() ) {
add_action( 'load-plugins.php', [ $this, 'plugin_licenses_warning' ] );
}
if ( ! wp_doing_ajax() ) {
add_action( 'admin_init', [ $this, 'add_on_updater' ], 1 );
}
add_action( 'advanced-ads-settings-init', [ $this, 'add_license_fields' ], 99 );
}
/**
* Register the Updater class for every add-on, which includes getting version information
*/
public function add_on_updater() {
// Ignore, if not main blog or if updater was disabled.
if ( ( is_multisite() && ! is_main_site() ) || ! apply_filters( 'advanced-ads-add-ons-updater', true ) ) {
return;
}
$add_ons = Data::get_addons();
foreach ( $add_ons as $_add_on ) {
$_add_on_key = $_add_on['id'];
$options_slug = $_add_on['options_slug'];
// Check if a license expired over time.
$expiry_date = $this->manager->get_license_expires( $options_slug );
$now = time();
if ( $expiry_date && 'lifetime' !== $expiry_date && strtotime( $expiry_date ) < $now ) {
// Remove license status.
delete_option( $options_slug . '-license-status' );
}
// Retrieve our license key.
$licenses = get_option( ADVADS_SLUG . '-licenses', [] );
$license_key = $licenses[ $_add_on_key ] ?? '';
( new EDD_Updater(
Constants::API_ENDPOINT,
$_add_on['path'],
[
'version' => $_add_on['version'],
'license' => $license_key,
'item_id' => Constants::ADDON_SLUGS_ID[ $options_slug ] ?? false,
'author' => 'Advanced Ads',
]
) );
}
}
/**
* Initiate plugin checks
*
* @since 1.7.12
*
* @return void
*/
public function plugin_licenses_warning(): void {
if ( is_multisite() ) {
return;
}
$add_ons = Data::get_addons();
foreach ( $add_ons as $_add_on ) {
if ( $this->manager->get_license_status( $_add_on['options_slug'] ) !== 'valid' ) {
$plugin_file = plugin_basename( $_add_on['path'] );
add_action( 'after_plugin_row_' . $plugin_file, [ $this, 'add_plugin_list_license_notice' ], 10, 2 );
}
}
}
/**
* Add a row below add-ons with an invalid license on the plugin list
*
* @param string $plugin_file Path to the plugin file, relative to the plugins directory.
* @param array $plugin_data An array of plugin data.
*
* @since 1.7.12
* @todo make this work on multisite as well
*
* @return void
*/
public function add_plugin_list_license_notice( $plugin_file, $plugin_data ): void {
static $cols;
if ( null === $cols ) {
$cols = count( _get_list_table( 'WP_Plugins_List_Table' )->get_columns() );
}
printf(
'<tr class="advads-plugin-update-tr plugin-update-tr active"><td class="plugin-update colspanchange" colspan="%d"><div class="update-message notice inline notice-warning notice-alt"><p>%s</p></div></td></tr>',
esc_attr( $cols ),
wp_kses_post(
sprintf(
/* Translators: 1: add-on name 2: admin URL to license page */
__( 'There might be a new version of %1$s. Please <strong>provide a valid license key</strong> in order to receive updates and support <a href="%2$s">on this page</a>.', 'advanced-ads' ),
$plugin_data['Title'],
admin_url( 'admin.php?page=advanced-ads-settings#top#licenses' )
)
)
);
}
/**
* Add license fields to the settings page.
*
* @return void
*/
public function add_license_fields(): void {
$add_ons = Data::get_addons();
foreach ( $add_ons as $data ) {
if ( 'responsive' === $data['id'] ) {
continue;
}
add_settings_field(
$data['id'] . '-license',
$data['name'],
[ $this, 'render_license_field' ],
'advanced-ads-settings-license-page',
'advanced_ads_settings_license_section',
$data
);
}
}
/**
* Render license key field
*
* @param array $data add-on data.
*
* @return void
*/
public function render_license_field( $data ): void {
$id = $data['id'];
$licenses = $this->manager->get_licenses();
$license_key = $licenses[ $id ] ?? '';
$options_slug = $data['options_slug'];
$license_status = $this->manager->get_license_status( $data['options_slug'] );
$index = $id;
$plugin_name = $data['name'];
$plugin_url = $data['uri'];
include ADVADS_ABSPATH . 'admin/views/setting-license.php';
}
}

View File

@@ -0,0 +1,261 @@
<?php
/**
* The class is responsible for adding menu and submenu pages for the plugin in the WordPress admin area.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin;
use Advanced_Ads_Checks;
use Advanced_Ads_Ad_Health_Notices;
use AdvancedAds\Constants;
use AdvancedAds\Admin\Pages;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Admin Menu.
*/
class Admin_Menu implements Integration_Interface {
/**
* Hold screens
*
* @var array
*/
private $screens = [];
/**
* Hold screen hooks
*
* @var array
*/
private $screen_ids = null;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_menu', [ $this, 'add_pages' ], 15 );
add_action( 'admin_head', [ $this, 'highlight_menu_item' ] );
add_filter( 'admin_body_class', [ $this, 'add_body_class' ] );
}
/**
* Register the administration menu for this plugin into the WordPress Dashboard menu.
*
* @since 1.0.0
*
* @return void
*/
public function add_pages(): void {
foreach ( $this->get_screens() as $renderer ) {
$renderer->register_screen();
}
$this->register_forward_links();
/**
* Allows extensions to insert sub menu pages.
*
* @deprecated 2.0.0 use `advanced-ads-add-screen` instead.
*
* @param string $plugin_slug The slug slug used to add a visible page.
* @param string $hidden_page_slug The slug slug used to add a hidden page.
*/
do_action( 'advanced-ads-submenu-pages', ADVADS_SLUG, 'advanced_ads_hidden_page_slug' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}
/**
* Register forward links
*
* @return void
*/
private function register_forward_links(): void {
global $submenu;
$has_ads = WordPress::get_count_ads();
$notices = Advanced_Ads_Ad_Health_Notices::get_number_of_notices();
$notice_alert = '&nbsp;<span class="update-plugins count-' . $notices . '"><span class="update-count">' . $notices . '</span></span>';
// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
if ( current_user_can( Conditional::user_cap( 'advanced_ads_manage_options' ) ) ) {
$submenu['advanced-ads'][] = [
__( 'Support', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_manage_options' ),
admin_url( 'admin.php?page=advanced-ads-settings#top#support' ),
__( 'Support', 'advanced-ads' ),
];
if ( $has_ads ) {
$submenu['advanced-ads'][0][0] .= $notice_alert;
} else {
$submenu['advanced-ads'][1][0] .= $notice_alert;
}
// Link to license tab if they are invalid.
if ( Advanced_Ads_Checks::licenses_invalid() ) {
$submenu['advanced-ads'][] = [
__( 'Licenses', 'advanced-ads' )
. '&nbsp;<span class="update-plugins count-1"><span class="update-count">!</span></span>',
Conditional::user_cap( 'advanced_ads_manage_options' ),
admin_url( 'admin.php?page=advanced-ads-settings#top#licenses' ),
__( 'Licenses', 'advanced-ads' ),
];
}
}
// phpcs:enable
}
/**
* Get a screen by its id
*
* @param string $id Screen id.
*
* @return Screen|null
*/
public function get_screen( string $id ) {
$screens = $this->get_screens();
return $screens[ $id ] ?? null;
}
/**
* Get the hook of a screen by its id
*
* @param string $id Screen id.
*
* @return string|null
*/
public function get_hook( $id ) {
$screen = $this->get_screen( $id );
return $screen ? $screen->get_hook() : null;
}
/**
* Add a screen to the list of screens
*
* @param string $screen Screen class name.
*
* @return void
*/
public function add_screen( string $screen ): void {
$screen = new $screen();
$this->screens[ $screen->get_id() ] = $screen;
}
/**
* Get screens
*
* @return array
*/
public function get_screens(): array {
if ( ! empty( $this->screens ) ) {
return $this->screens;
}
$this->add_screen( Pages\Dashboard::class );
$this->add_screen( Pages\Ads::class );
$this->add_screen( Pages\Ads_Editing::class );
$this->add_screen( Pages\Groups::class );
$this->add_screen( Pages\Placements::class );
$this->add_screen( Pages\Settings::class );
$this->add_screen( Pages\Tools::class );
$this->add_screen( Pages\Onboarding::class );
if ( defined( 'ADVADS_UI_KIT' ) && ADVADS_UI_KIT ) {
$this->add_screen( Pages\Ui_Toolkit::class );
}
/**
* Let developers add their own screens.
*
* @param array $screens
*/
do_action( 'advanced-ads-add-screen', $this );
// Order screens using the order property.
uasort(
$this->screens,
static function ( $a, $b ) {
$order_a = $a->get_order();
$order_b = $b->get_order();
if ( $order_a === $order_b ) {
return 0;
}
return ( $order_a < $order_b ) ? -1 : 1;
}
);
return $this->screens;
}
/**
* Get screen ids
*
* @return array
*/
public function get_screen_ids(): array {
if ( null !== $this->screen_ids ) {
return $this->screen_ids;
}
$screens = $this->get_screens();
foreach ( $screens as $screen ) {
$this->screen_ids[] = $screen->get_hook();
}
return $this->screen_ids;
}
/**
* Highlights the 'Advanced Ads->Ads' item in the menu when an ad edit page is open
*
* @see the 'parent_file' and the 'submenu_file' filters for reference
*
* @return void
*/
public function highlight_menu_item(): void {
global $parent_file, $submenu_file, $post_type;
// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
if ( Constants::POST_TYPE_AD === $post_type ) {
$parent_file = ADVADS_SLUG;
$submenu_file = 'edit.php?post_type=' . Constants::POST_TYPE_AD;
}
// phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
}
/**
* Add a custom class to the body tag of Advanced Ads screens.
*
* @param string $classes Space-separated class list.
*
* @return string
*/
public function add_body_class( $classes ): string {
// Ensure $classes is always a string due to 3rd party plugins interfering with the filter.
$classes = is_string( $classes ) ? $classes : '';
$screen_ids = $this->get_screen_ids();
$wp_screen = get_current_screen();
if ( in_array( $wp_screen->id, $screen_ids, true ) ) {
$classes .= ' advads-page';
}
return $classes;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Admin Notices.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Notices.
*/
class Admin_Notices implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'all_admin_notices', [ $this, 'create_first_ad' ] );
add_action( 'admin_notices', [ $this, 'show_rollback_notice' ] );
}
/**
* Show instructions to create first ad above the ad list
*
* @return void
*/
public function create_first_ad(): void {
$screen = get_current_screen();
if ( ! isset( $screen->id ) || 'edit-advanced_ads' !== $screen->id ) {
return;
}
$counts = WordPress::get_count_ads();
// Only display if there are no more than 2 ads.
if ( 3 > $counts ) {
include ADVADS_ABSPATH . 'views/notices/create-first-ad.php';
}
}
/**
* Show notice to rollback to a previous version.
*
* @return void
*/
public function show_rollback_notice(): void {
// show only on plugins page.
if ( 'plugins' !== get_current_screen()->id ) {
return;
}
$rollback = filter_input( INPUT_GET, 'rollback', FILTER_VALIDATE_BOOLEAN );
if ( ! $rollback ) {
return;
}
$rollback_notification = defined( 'ADVADS_VERSION' )
/* translators: %s: version number */
? sprintf( esc_html__( 'You have successfully rolled back to Advanced Ads %s', 'advanced-ads' ), ADVADS_VERSION )
: esc_html__( 'You have successfully rolled back to a previous version of Advanced Ads.', 'advanced-ads' );
?>
<div class="notice notice-success is-dismissible">
<p>
<?php echo esc_html( $rollback_notification ); ?>
</p>
</div>
<?php
}
}

View File

@@ -0,0 +1,991 @@
<?php
/**
* AJAX Ads
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use Advanced_Ads_Pro;
use Advanced_Ads_Privacy;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use Advanced_Ads_Admin_Notices;
use AdvancedAds\Frontend\Stats;
use Advanced_Ads_Admin_Licenses;
use Advanced_Ads_Ad_Blocker_Admin;
use Advanced_Ads_Ad_Health_Notices;
use Advanced_Ads_Display_Conditions;
use Advanced_Ads_Visitor_Conditions;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Arr;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Frontend AJAX.
*/
class AJAX implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'delete_post', [ $this, 'delete_ad' ] );
add_action( 'wp_ajax_advads_ad_select', [ $this, 'ad_select' ] );
add_action( 'wp_ajax_nopriv_advads_ad_select', [ $this, 'ad_select' ] );
add_action( 'wp_ajax_advads-ad-health-notice-push', [ $this, 'ad_health_notice_push' ] );
add_action( 'wp_ajax_nopriv_advads-ad-health-notice-push', [ $this, 'ad_health_notice_push' ] );
add_action( 'wp_ajax_advads_dismiss_welcome', [ $this, 'dismiss_welcome' ] );
add_action( 'wp_ajax_advads_newsletter', [ $this, 'subscribe_to_newsletter' ] );
add_action( 'wp_ajax_advads_activate_addon', [ $this, 'activate_add_on' ] );
add_action( 'wp_ajax_advads-multiple-subscribe', [ $this, 'multiple_subscribe' ] );
add_action( 'wp_ajax_load_ad_parameters_metabox', [ $this, 'load_ad_parameters_metabox' ] );
add_action( 'wp_ajax_load_visitor_conditions_metabox', [ $this, 'load_visitor_condition' ] );
add_action( 'wp_ajax_load_display_conditions_metabox', [ $this, 'load_display_condition' ] );
add_action( 'wp_ajax_advads-terms-search', [ $this, 'search_terms' ] );
add_action( 'wp_ajax_advads-authors-search', [ $this, 'search_authors' ] );
add_action( 'wp_ajax_advads-close-notice', [ $this, 'close_notice' ] );
add_action( 'wp_ajax_advads-hide-notice', [ $this, 'hide_notice' ] );
add_action( 'wp_ajax_advads-subscribe-notice', [ $this, 'subscribe' ] );
add_action( 'wp_ajax_advads-activate-license', [ $this, 'activate_license' ] );
add_action( 'wp_ajax_advads-deactivate-license', [ $this, 'deactivate_license' ] );
add_action( 'wp_ajax_advads-adblock-rebuild-assets', [ $this, 'adblock_rebuild_assets' ] );
add_action( 'wp_ajax_advads-post-search', [ $this, 'post_search' ] );
add_action( 'wp_ajax_advads-ad-injection-content', [ $this, 'inject_placement' ] );
add_action( 'wp_ajax_advads-save-hide-wizard-state', [ $this, 'save_wizard_state' ] );
add_action( 'wp_ajax_advads-adsense-enable-pla', [ $this, 'adsense_enable_pla' ] );
add_action( 'wp_ajax_advads-ad-health-notice-display', [ $this, 'ad_health_notice_display' ] );
add_action( 'wp_ajax_advads-ad-health-notice-push-adminui', [ $this, 'ad_health_notice_push_adminui' ] );
add_action( 'wp_ajax_advads-ad-health-notice-hide', [ $this, 'ad_health_notice_hide' ] );
add_action( 'wp_ajax_advads-ad-health-notice-unignore', [ $this, 'ad_health_notice_unignore' ] );
add_action( 'wp_ajax_advads-ad-health-notice-solved', [ $this, 'ad_health_notice_solved' ] );
add_action( 'wp_ajax_advads-update-frontend-element', [ $this, 'update_frontend_element' ] );
add_action( 'wp_ajax_advads-get-block-hints', [ $this, 'get_block_hints' ] );
add_action( 'wp_ajax_advads-placements-allowed-ads', [ $this, 'get_allowed_ads_for_placement_type' ] );
add_action( 'wp_ajax_advads-placement-update-item', [ $this, 'placement_update_item' ] );
}
/**
* Prepare the ad post type to be removed
*
* @param int $post_id id of the post.
*
* @return void
*/
public function delete_ad( $post_id ): void {
global $wpdb;
if ( ! current_user_can( 'delete_posts' ) ) {
return;
}
if ( $post_id > 0 ) {
$post_type = get_post_type( $post_id );
if ( Constants::POST_TYPE_AD === $post_type ) {
/**
* Images uploaded to an image ad type get the `_advanced-ads_parent_id` meta key from WordPress automatically
* the following SQL query removes that meta data from any attachment when the ad is removed.
*/
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %d", '_advanced-ads_parent_id', $post_id ) ); // phpcs:ignore
}
}
}
/**
* Background plugin activation from the add-on box
*
* @return void
*/
public function activate_add_on(): void {
wp_ajax_activate_plugin();
}
/**
* Subscribe to the newsletter
*
* @return void
*/
public function subscribe_to_newsletter(): void {
if ( ! wp_verify_nonce( sanitize_text_field( Params::post( 'nonce' ), '' ), 'advads-newsletter-subscribe' ) ) {
wp_send_json_error( 'Not Authorized', 401 );
}
if ( ! Conditional::user_can( 'advanced_ads_see_interface' ) ) {
wp_send_json_error(
[
/* translators: %s is a URL. */
'message' => sprintf( __( 'An error occurred. Please use <a href="%s" target="_blank">this form</a> to sign up.', 'advanced-ads' ), 'http://eepurl.com/bk4z4P' ),
],
403
);
}
wp_send_json_success( \Advanced_Ads_Admin_Notices::get_instance()->subscribe( 'nl_free_addons' ), 200 );
}
/**
* Stop showing the welcome after a click on the dismiss icon
*
* @return void
*/
public function dismiss_welcome(): void {
Welcome::get()->dismiss();
wp_send_json_success( 'OK', 200 );
}
/**
* Simple wp ajax interface for ad selection.
*
* @return void
*/
public function ad_select(): void {
add_filter( 'advanced-ads-output-inline-css', '__return_false' );
// Allow modules / add-ons to test (this is rather late but should happen before anything important is called).
do_action( 'advanced-ads-ajax-ad-select-init' );
$ad_ids = Params::request( 'ad_ids', [], FILTER_VALIDATE_INT, FILTER_REQUIRE_ARRAY );
$defered_ads = Params::request( 'deferedAds', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
if ( is_array( $ad_ids ) ) {
foreach ( $ad_ids as $ad_id ) {
Stats::get()->add_entity( 'ad', is_array( $ad_id ) ? $ad_id['id'] : $ad_id, '' );
}
}
if ( $defered_ads ) {
$response = [];
$requests_by_blog = [];
foreach ( $defered_ads as $request ) {
$blog_id = $request['blog_id'] ?? get_current_blog_id();
$requests_by_blog[ $blog_id ][] = $request;
}
foreach ( $requests_by_blog as $blog_id => $requests ) {
if ( get_current_blog_id() !== $blog_id && is_multisite() ) {
switch_to_blog( $blog_id );
}
foreach ( $requests as $request ) {
$result = $this->select_one( $request );
$result['elementId'] = $request['elementId'] ?? null;
$response[] = $result;
}
if ( get_current_blog_id() !== $blog_id && is_multisite() ) {
restore_current_blog();
}
}
wp_send_json( $response );
}
$response = $this->select_one( $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_send_json( $response );
}
/**
* Push an Ad Health notice to the queue in the backend
*
* @return void
*/
public function ad_health_notice_push(): void {
check_ajax_referer( 'advanced-ads-ad-health-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
$key = ! empty( $_REQUEST['key'] ) ? esc_attr( Params::request( 'key' ) ) : false;
$attr = Params::request( 'attr', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
// Update or new entry?
if ( isset( $attr['mode'] ) && 'update' === $attr['mode'] ) {
Advanced_Ads_Ad_Health_Notices::get_instance()->update( $key, $attr );
} else {
Advanced_Ads_Ad_Health_Notices::get_instance()->add( $key, $attr );
}
die();
}
/**
* Check if AJAX ad can be displayed, with consent information sent in request.
*
* @param bool $can_display Whether this ad can be displayed.
* @param Ad $ad The ad object.
*
* @return bool
*/
public function can_display_by_consent( $can_display, $ad ) {
// Early bail!!
if ( ! $can_display ) {
return $can_display;
}
// If consent is overridden for the ad.
$privacy_props = $ad->get_prop( 'privacy' );
if ( ! empty( $privacy_props['ignore-consent'] ) ) {
return true;
}
// If privacy module is not active, we can display.
if ( empty( Advanced_Ads_Privacy::get_instance()->options()['enabled'] ) ) {
return true;
}
$consent_state = Params::request( 'consent', 'not_allowed' );
// Consent is either given or not needed.
if ( in_array( $consent_state, [ 'not_needed', 'accepted' ], true ) ) {
return true;
}
// If there is custom code, don't display the ad (unless it's a group).
if (
class_exists( 'Advanced_Ads_Pro' ) &&
! empty( Advanced_Ads_Pro::get_instance()->get_custom_code( $ad ) ) &&
! $ad->is_type( 'group' )
) {
return false;
}
// See if this ad type needs consent.
return ! Advanced_Ads_Privacy::get_instance()->ad_type_needs_consent( $ad->get_type() );
}
/**
* Subscribe to multiple newsletters
*/
public function multiple_subscribe() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$groups = ! empty( Params::post( 'groups' ) ) ? json_decode( Params::post( 'groups' ), true ) : [];
if ( ! Conditional::user_can( 'advanced_ads_see_interface' ) || empty( $groups ) ) {
wp_send_json_error(
[
/* translators: %s is a URL. */
'message' => sprintf( __( 'An error occurred. Please use <a href="%s" target="_blank">this form</a> to sign up.', 'advanced-ads' ), 'http://eepurl.com/bk4z4P' ),
],
400
);
}
foreach ( $groups as $group ) {
$message = Advanced_Ads_Admin_Notices::get_instance()->subscribe( $group );
}
wp_send_json_success( [ 'message' => $message ?? '' ] );
}
/**
* Provides a single ad (ad, group, placement) given ID and selection method.
*
* @param array $request Request.
*
* @return array
*/
private function select_one( $request ) {
$method = (string) $request['ad_method'] ?? null;
if ( 'id' === $method ) {
$method = 'ad';
}
// Early bail!!
if ( ! Conditional::is_entity_allowed( $method ) ) {
return [
'status' => 'error',
'message' => __( 'The method is not allowed to render.', 'advanced-ads' ),
];
}
$function = "get_the_$method";
$id = (string) $request['ad_id'] ?? null;
$arguments = $request['ad_args'] ?? [];
if ( is_string( $arguments ) ) {
$arguments = stripslashes( $arguments );
$arguments = json_decode( $arguments, true );
}
if ( ! empty( $request['elementId'] ) ) {
$arguments['cache_busting_elementid'] = $request['elementId'];
}
// Report error.
if ( empty( $id ) || ! function_exists( $function ) ) {
return [
'status' => 'error',
'message' => 'No valid ID or METHOD found.',
];
}
/**
* Filters the received arguments before passing them to ads/groups/placements.
*
* @param array $arguments Existing arguments.
* @param array $request Request data.
*/
$arguments = apply_filters( 'advanced-ads-ajax-ad-select-arguments', $arguments, $request );
$previous_ads = Stats::get()->entities;
add_filter( 'advanced-ads-can-display-ad', [ $this, 'can_display_by_consent' ], 10, 2 );
$content = $function( (int) $id, '', $arguments );
if ( empty( $content ) ) {
return [
'status' => 'error',
'message' => 'No displayable ad found for privacy settings.',
];
}
$response = [
'status' => 'success',
'item' => $content,
'id' => $id,
'method' => $method,
'ads' => array_slice( Stats::get()->entities, count( $previous_ads ) ),
'blog_id' => get_current_blog_id(),
];
return apply_filters(
'advanced-ads-cache-busting-item',
$response,
[
'id' => $id,
'method' => $method,
'args' => $arguments,
]
);
}
/**
* Load content of the ad parameter metabox
*
* @since 1.0.0
*/
public function load_ad_parameters_metabox() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
$type_string = Params::post( 'ad_type' );
$ad_id = Params::post( 'ad_id', 0, FILTER_VALIDATE_INT );
if ( empty( $ad_id ) ) {
die();
}
if ( wp_advads_has_ad_type( $type_string ) ) {
$ad = wp_advads_get_ad( $ad_id, $type_string );
$ad_type = wp_advads_get_ad_type( $type_string );
if ( method_exists( $ad_type, 'render_parameters' ) ) {
$ad_type->render_parameters( $ad );
}
if ( $ad_type->has_size() ) {
include ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-parameters-size.php';
}
// Extend the AJAX-loaded parameters form by ad type.
do_action( "advanced-ads-ad-params-after-{$ad->get_type()}", $ad );
}
die();
}
/**
* Load interface for single visitor condition
*
* @since 1.5.4
*/
public function load_visitor_condition() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
// get visitor condition types.
$visitor_conditions = Advanced_Ads_Visitor_Conditions::get_instance()->conditions;
$condition = [];
$condition['type'] = Params::post( 'type', '' );
$index = Params::post( 'index', 0, FILTER_VALIDATE_INT );
$form_name = Params::post( 'form_name', Advanced_Ads_Visitor_Conditions::FORM_NAME );
if ( ! isset( $visitor_conditions[ $condition['type'] ] ) ) {
die();
}
$metabox = $visitor_conditions[ $condition['type'] ]['metabox'];
if ( method_exists( $metabox[0], $metabox[1] ) ) {
call_user_func( [ $metabox[0], $metabox[1] ], $condition, $index, $form_name );
}
die();
}
/**
* Load interface for single display condition
*
* @since 1.7
*/
public function load_display_condition() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
// get display condition types.
$conditions = Advanced_Ads_Display_Conditions::get_instance()->conditions;
$condition = [];
$condition['type'] = Params::post( 'type', '' );
$index = Params::post( 'index', 0, FILTER_VALIDATE_INT );
$form_name = Params::post( 'form_name', Advanced_Ads_Display_Conditions::FORM_NAME );
if ( ! isset( $conditions[ $condition['type'] ] ) ) {
die();
}
$metabox = $conditions[ $condition['type'] ]['metabox'];
if ( method_exists( $metabox[0], $metabox[1] ) ) {
call_user_func( [ $metabox[0], $metabox[1] ], $condition, $index, $form_name );
}
die();
}
/**
* Search terms belonging to a specific taxonomy
*
* @since 1.4.7
*/
public function search_terms() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
$args = [
'taxonomy' => Params::post( 'tax', '' ),
'hide_empty' => false,
'number' => 20,
];
$search = Params::post( 'search', '' );
if ( '' === $search ) {
die();
}
// if search is an id, search for the term id, else do a full text search.
if ( 0 !== absint( $search ) && strlen( $search ) === strlen( absint( $search ) ) ) {
$args['include'] = [ absint( $search ) ];
} else {
$args['search'] = $search;
}
$results = get_terms( $args );
echo wp_json_encode( $results );
echo "\n";
die();
}
/**
* Search authors
*
* @since 1.47.5
*/
public function search_authors() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
$args['search_columns'] = [ 'ID', 'user_login', 'user_nicename', 'display_name' ];
if ( version_compare( get_bloginfo( 'version' ), '5.9' ) > -1 ) {
$args['capability'] = [ 'edit_posts' ];
} else {
$args['who'] = 'authors';
}
$search = Params::post( 'search', '' );
if ( '' === $search ) {
die();
}
$args['search'] = '*' . sanitize_text_field( wp_unslash( $search ) ) . '*';
$results = get_users( $args );
echo wp_json_encode( $results );
die();
}
/**
* Close a notice for good
*
* @since 1.5.3
*/
public function close_notice() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$notice = Params::request( 'notice' );
if (
! Conditional::user_can( 'advanced_ads_manage_options' )
|| empty( $notice )
) {
die();
}
Advanced_Ads_Admin_Notices::get_instance()->remove_from_queue( $notice );
// permanent dismissed.
if ( 'monetize_wizard' === Params::request( 'notice' ) ) {
update_user_meta( get_current_user_id(), Constants::USER_WIZARD_DISMISS, true );
}
$redirect = Params::request( 'redirect' );
if ( $redirect && wp_safe_redirect( $redirect ) ) {
exit();
}
die();
}
/**
* Hide a notice for some time (7 days right now)
*
* @since 1.8.17
*/
public function hide_notice() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$notice = Params::request( 'notice' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' )
|| empty( $notice )
) {
die();
}
Advanced_Ads_Admin_Notices::get_instance()->hide_notice( $notice );
die();
}
/**
* Subscribe to newsletter
*
* @since 1.5.3
*/
public function subscribe() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$notice = Params::request( 'notice' );
if (
! Conditional::user_can( 'advanced_ads_see_interface' )
|| empty( $notice )
) {
wp_send_json_error(
[
/* translators: %s is a URL. */
'message' => sprintf( __( 'An error occurred. Please use <a href="%s" target="_blank">this form</a> to sign up.', 'advanced-ads' ), 'http://eepurl.com/bk4z4P' ),
],
400
);
}
wp_send_json_success( [ 'message' => Advanced_Ads_Admin_Notices::get_instance()->subscribe( $notice ) ] );
}
/**
* Activate license of an add-on
*
* @since 1.5.7
*/
public function activate_license() {
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
check_ajax_referer( 'advads_ajax_license_nonce', 'security' );
$addon = Params::post( 'addon' );
if ( '' === $addon ) {
die();
}
// phpcs:disable
echo Advanced_Ads_Admin_Licenses::get_instance()->activate_license(
$addon,
Params::post( 'pluginname' ),
Params::post( 'optionslug' ),
Params::post( 'license' )
);
// phpcs:enable
die();
}
/**
* Deactivate license of an add-on
*
* @since 1.6.11
*/
public function deactivate_license() {
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
check_ajax_referer( 'advads_ajax_license_nonce', 'security' );
$addon = Params::post( 'addon' );
if ( '' === $addon ) {
die();
}
// phpcs:disable
echo Advanced_Ads_Admin_Licenses::get_instance()->deactivate_license(
$addon,
Params::post( 'pluginname' ),
Params::post( 'optionslug' )
);
// phpcs:enable
die();
}
/**
* Rebuild assets for ad-blocker module
*/
public function adblock_rebuild_assets() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
Advanced_Ads_Ad_Blocker_Admin::get_instance()->add_asset_rebuild_form();
die();
}
/**
* Post search (used in Display conditions)
*/
public function post_search() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
add_filter( 'wp_link_query_args', [ 'Advanced_Ads_Display_Conditions', 'modify_post_search' ] );
add_filter( 'posts_search', [ 'Advanced_Ads_Display_Conditions', 'modify_post_search_sql' ] );
wp_ajax_wp_link_ajax();
}
/**
* Inject an ad and a placement
*
* @since 1.7.3
*/
public function inject_placement() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$ad_id = Params::request( 'ad_id', 0, FILTER_VALIDATE_INT );
// Early bail!!
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) || ! $ad_id ) {
die();
}
// use existing placement.
$placement_id = Params::request( 'placement_id', 0, FILTER_VALIDATE_INT );
if ( $placement_id ) {
$placement = wp_advads_get_placement( $placement_id );
if ( $placement ) {
$current_item = $placement->get_item();
// Check if current item is a group and new item is an ad.
if ( is_string( $current_item ) && strpos( $current_item, 'group_' ) === 0 ) {
$group = wp_advads_get_group( (int) str_replace( 'group_', '', $current_item ) );
if ( $group ) {
$ad_weights = $group->get_ad_weights();
$ad_weights[ $ad_id ] = Constants::GROUP_AD_DEFAULT_WEIGHT;
$group->set_ad_weights( $ad_weights );
$group->save();
}
} else {
$placement->set_item( 'ad_' . $ad_id );
$placement->save();
}
echo esc_attr( $placement_id );
}
die();
}
$type = esc_attr( Params::request( 'placement_type' ) );
if ( ! wp_advads_has_placement_type( $type ) ) {
die();
}
$new_placement = wp_advads_create_new_placement( $type );
$props = [
'item' => 'ad_' . $ad_id,
'title' => wp_advads_get_placement_type( $type )->get_title(),
];
// set content specific options.
if ( $new_placement->is_type( 'post_content' ) ) {
$options = Params::request( 'options', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$index = (int) Arr::get( $options, 'index', 1 );
$props['position'] = 'after';
$props['index'] = $index;
$props['tag'] = 'p';
}
$new_placement->set_props( $props );
echo $new_placement->save();; // phpcs:ignore
}
/**
* Save ad wizard state for each user individually
*
* @since 1.7.4
*/
public function save_wizard_state() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
return;
}
$user_id = get_current_user_id();
if ( ! $user_id ) {
die();
}
$state = 'true' === Params::request( 'hideWizard' ) ? 'true' : 'false';
update_user_meta( $user_id, 'advanced-ads-hide-wizard', $state );
die();
}
/**
* Enable Adsense Auto ads, previously "Page-Level ads"
*/
public function adsense_enable_pla() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
$options = get_option( GADSENSE_OPT_NAME, [] );
$options['page-level-enabled'] = true;
update_option( GADSENSE_OPT_NAME, $options );
die();
}
/**
* Display list of Ad Health notices
*/
public function ad_health_notice_display() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
Advanced_Ads_Ad_Health_Notices::get_instance()->render_widget();
die();
}
/**
* Push an Ad Health notice to the queue
*/
public function ad_health_notice_push_adminui() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
$key = Params::request( 'key' );
$attr = Params::request( 'attr', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$attr = ! empty( $attr ) && is_array( $attr ) ? $attr : [];
// update or new entry?
if ( isset( $attr['mode'] ) && 'update' === $attr['mode'] ) {
Advanced_Ads_Ad_Health_Notices::get_instance()->update( $key, $attr );
} else {
Advanced_Ads_Ad_Health_Notices::get_instance()->add( $key, $attr );
}
die();
}
/**
* Hide Ad Health notice
*/
public function ad_health_notice_hide() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
$notice = Params::request( 'notice', '' );
$notice_key = ! empty( $notice ) ? esc_attr( $notice ) : false;
Advanced_Ads_Ad_Health_Notices::get_instance()->hide( $notice_key );
die();
}
/**
* Show all ignored notices of a given type
*/
public function ad_health_notice_unignore() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
return;
}
Advanced_Ads_Ad_Health_Notices::get_instance()->unignore();
die();
}
/**
* After the user has selected a new frontend element, update the corresponding placement.
*/
public function update_frontend_element() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
if ( ! Conditional::user_can( 'advanced_ads_manage_placements' ) ) {
return;
}
$return = wp_update_post( $_POST );
if ( is_wp_error( $return ) ) {
wp_send_json_error( [ 'error' => $return->get_error_message() ], 400 );
}
wp_send_json_success( [ 'id' => $return ] );
}
/**
* Get hints related to the Gutenberg block.
*/
public function get_block_hints() {
check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
$item = Params::post( 'itemID' );
if ( ! $item || ! Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
die;
}
$item = explode( '_', $item );
if ( ! isset( $item[0] ) || 'group' !== $item[0] ) {
die;
}
$group = wp_advads_get_group( absint( $item[1] ) );
if ( ! $group ) {
die;
}
wp_send_json_success( $group->get_hints() );
}
/**
* Get allowed ads per placement.
*
* @return void
*/
public function get_allowed_ads_for_placement_type() {
check_ajax_referer( sanitize_text_field( Params::post( 'action', '' ) ) );
$placement_type = wp_advads_get_placement_type( sanitize_text_field( Params::post( 'placement_type' ) ) );
wp_send_json_success(
[
'items' => array_filter(
$placement_type->get_allowed_items(),
static function ( $items_group ) {
return ! empty( $items_group['items'] );
}
),
]
);
}
/**
* Update the item for the placement.
*
* @return void
*/
public function placement_update_item(): void {
$placement = wp_advads_get_placement( Params::post( 'placement_id', false, FILTER_VALIDATE_INT ) );
$new_item = sanitize_text_field( Params::post( 'item_id' ) );
$new_item_type = 0 === strpos( $new_item, 'ad' ) ? 'ad_' : 'group_';
try {
if ( empty( $new_item ) ) {
$placement->remove_item();
wp_send_json_success(
[
'edit_href' => '#',
'placement_id' => $placement->get_id(),
'item_id' => '',
]
);
}
$new_item = $placement->update_item( $new_item );
wp_send_json_success(
[
'edit_href' => $new_item->get_edit_link(),
'placement_id' => $placement->get_id(),
'item_id' => $new_item_type . $new_item->get_id(),
]
);
} catch ( \RuntimeException $e ) {
wp_send_json_error(
[
'message' => $e->getMessage(),
'item_id' => $placement->get_item_object() ? $placement->get_item_object()->get_id() : 0,
],
400
);
}
}
}

View File

@@ -0,0 +1,215 @@
<?php
/**
* Assets manages the enqueuing of styles and scripts for the administration area.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Constants;
use Advanced_Ads_AdSense_Admin;
use Advanced_Ads_Display_Conditions;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Assets.
*/
class Assets implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_enqueue_scripts', [ $this, 'current_screen' ], 10, 0 );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ], 10, 0 );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ], 9, 0 );
}
/**
* Enqueue styles and scripts for current screen
*
* @return void
*/
public function current_screen(): void {
$screens = wp_advads()->screens->get_screens();
$wp_screen = get_current_screen();
foreach ( $screens as $screen ) {
if ( $wp_screen->id === $screen->get_hook() ) {
$screen->enqueue_assets();
do_action( 'advanced-ads-screen-' . $screen->get_id(), $screen );
}
}
}
/**
* Enqueue styles
*
* @return void
*/
public function enqueue_styles(): void {
$wp_screen = get_current_screen();
if ( 'post' === $wp_screen->base && Constants::POST_TYPE_AD === $wp_screen->post_type ) {
wp_advads()->registry->enqueue_style( 'screen-ads-editing' );
wp_advads()->registry->enqueue_script( 'screen-ads-editing' );
// Enqueue code editor and settings for manipulating HTML.
$settings = wp_enqueue_code_editor( [ 'type' => 'application/x-httpd-php' ] );
// Only if CodeMirror is enabled.
if ( false !== $settings ) {
wp_advads()->json->add(
'admin',
[
'codeMirror' => [
'settings' => $settings,
],
]
);
}
}
// TODO: made them load conditionaly.
if ( 'dashboard' !== $wp_screen->id ) {
wp_advads()->registry->enqueue_style( 'ui' );
wp_advads()->registry->enqueue_style( 'admin' );
}
if ( 'post' === $wp_screen->base && Constants::POST_TYPE_AD === $wp_screen->post_type ) {
wp_advads()->registry->enqueue_style( 'ad-positioning' );
}
if ( Conditional::is_screen_advanced_ads() ) {
wp_advads()->registry->enqueue_style( 'notifications' );
}
}
/**
* Enqueue scripts
*
* @return void
*/
public function enqueue_scripts(): void {
global $post;
$screen = get_current_screen();
$this->enqueue_endpoints();
$this->enqueue_site_info();
// TODO: add conditional loading.
wp_advads()->registry->enqueue_script( 'admin-global' );
wp_advads()->registry->enqueue_script( 'find-adblocker' );
$params = [
'ajax_nonce' => wp_create_nonce( 'advanced-ads-admin-ajax-nonce' ),
'create_ad_url' => esc_url( admin_url( 'post-new.php?post_type=advanced_ads' ) ),
'create_your_first_ad' => __( 'Create your first ad', 'advanced-ads' ),
];
wp_advads_json_add( $params, 'advadsglobal' );
// TODO: remove later start using global data variable.
wp_advads_json_add( 'ajax_nonce', wp_create_nonce( 'advanced-ads-admin-ajax-nonce' ), 'advadsglobal' );
if ( Conditional::is_screen_advanced_ads() ) {
wp_advads()->registry->enqueue_script( 'admin' );
wp_advads()->registry->enqueue_script( 'conditions' );
wp_advads()->registry->enqueue_script( 'wizard' );
wp_advads()->registry->enqueue_script( 'adblocker-image-data' );
wp_advads()->registry->enqueue_script( 'notifications-center' );
$translation_array = [
'condition_or' => __( 'or', 'advanced-ads' ),
'condition_and' => __( 'and', 'advanced-ads' ),
'after_paragraph_promt' => __( 'After which paragraph?', 'advanced-ads' ),
'page_level_ads_enabled' => Advanced_Ads_AdSense_Admin::get_auto_ads_messages()['enabled'],
'today' => __( 'Today', 'advanced-ads' ),
'yesterday' => __( 'Yesterday', 'advanced-ads' ),
'this_month' => __( 'This Month', 'advanced-ads' ),
/* translators: 1: The number of days. */
'last_n_days' => __( 'Last %1$d days', 'advanced-ads' ),
/* translators: 1: An error message. */
'error_message' => __( 'An error occurred: %1$s', 'advanced-ads' ),
'all' => __( 'All', 'advanced-ads' ),
'active' => __( 'Active', 'advanced-ads' ),
'no_results' => __( 'There were no results returned for this ad. Please make sure it is active, generating impressions and double check your ad parameters.', 'advanced-ads' ),
'show_inactive_ads' => __( 'Show inactive ads', 'advanced-ads' ),
'hide_inactive_ads' => __( 'Hide inactive ads', 'advanced-ads' ),
'display_conditions_form_name' => Advanced_Ads_Display_Conditions::FORM_NAME, // not meant for translation.
'delete_placement_confirmation' => __( 'Permanently delete this placement?', 'advanced-ads' ),
'close' => __( 'Close', 'advanced-ads' ),
'close_save' => __( 'Close and save', 'advanced-ads' ),
'save_new_placement' => __( 'Save new placement', 'advanced-ads' ),
'confirmation' => __( 'Data you have entered has not been saved. Are you sure you want to discard your changes?', 'advanced-ads' ),
'admin_page' => $screen->id,
'placements_allowed_ads' => [
'action' => 'advads-placements-allowed-ads',
'nonce' => wp_create_nonce( 'advads-placements-allowed-ads' ),
],
'group_forms' => [
'save' => __( 'Save', 'advanced-ads' ),
'save_new' => __( 'Save New Group', 'advanced-ads' ),
'updated' => __( 'Group updated', 'advanced-ads' ),
'deleted' => __( 'Group deleted', 'advanced-ads' ),
/* translators: an ad group title. */
'confirmation' => __( 'You are about to permanently delete %s', 'advanced-ads' ),
],
'placement_forms' => [
'created' => __( 'New placement created', 'advanced-ads' ),
'updated' => __( 'Placement updated', 'advanced-ads' ),
],
];
// TODO: remove later start using global data variable.
wp_advads_json_add( $translation_array, 'advadstxt' );
}
if ( Constants::POST_TYPE_AD === $screen->id ) {
wp_enqueue_media( [ 'post' => $post ] );
}
// Ad edit screen.
if ( 'post' === $screen->base && Constants::POST_TYPE_AD === $screen->post_type ) {
wp_advads()->registry->enqueue_script( 'ad-positioning' );
}
if ( in_array( $screen->id, [ 'edit-post', 'edit-page' ], true ) && current_user_can( 'edit_posts' ) ) {
wp_advads()->registry->enqueue_script( 'page-quick-edit' );
wp_advads_json_add( 'page_quick_edit', [ 'nonce' => wp_create_nonce( 'advads-post-quick-edit' ) ] );
}
}
/**
* Global variables: advancedAds
*/
private function enqueue_site_info() {
$endpoints = [
'blogId' => get_current_blog_id(),
'homeUrl' => get_home_url(),
];
wp_advads_json_add( 'siteInfo', $endpoints );
}
/**
* Global variables: advancedAds
*/
private function enqueue_endpoints() {
$endpoints = [
'adminUrl' => esc_url( admin_url( '/' ) ),
'ajaxUrl' => esc_url( admin_url( 'admin-ajax.php' ) ),
'assetsUrl' => esc_url( ADVADS_BASE_URL ),
'editAd' => esc_url( admin_url( 'post.php?action=edit&post=' ) ),
];
wp_advads_json_add( 'endpoints', $endpoints );
}
}

View File

@@ -0,0 +1,195 @@
<?php
/**
* The class manages the ad authors.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin;
use Advanced_Ads;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
use WP_Role;
use WP_User_Query;
defined( 'ABSPATH' ) || exit;
/**
* Control Ad Authors.
*/
class Authors implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'wp_dropdown_users_args', [ $this, 'filter_ad_authors' ] );
add_action( 'pre_post_update', [ $this, 'sanitize_author_saving' ], 10, 2 );
add_filter( 'map_meta_cap', [ $this, 'filter_editable_posts' ], 10, 4 );
}
/**
* Ensure that users cannot assign ads to users with unfiltered_html if they don't have the capability themselves.
*
* @param array $query_args WP_User_Query args.
*
* @return array
*/
public function filter_ad_authors( $query_args ) {
$screen = get_current_screen();
if ( ! $screen || Constants::POST_TYPE_AD !== $screen->post_type ) {
return $query_args;
}
if ( is_multisite() ) {
return $this->multisite_filter_ad_authors( $query_args );
}
$user_roles_to_display = $this->filtered_user_roles();
$query_args['role__in'] = wp_list_pluck( $user_roles_to_display, 'name' );
return $query_args;
}
/**
* Ensure that users cannot assign ads to users who have more rights on multisite.
*
* @param array $query_args WP_User_Query args.
*
* @return array
*/
private function multisite_filter_ad_authors( $query_args ) {
if ( is_super_admin() ) {
return $query_args;
}
$options = Advanced_Ads::get_instance()->options();
$allowed_roles = $options['allow-unfiltered-html'] ?? [];
// if the current user can unfiltered_html, return the default args.
if ( ! empty( array_intersect( wp_get_current_user()->roles, $allowed_roles ) ) ) {
return $query_args;
}
// if the current user can't use unfiltered_html, they should not be able to assign the ad to a user that can.
$user_roles_to_display = array_filter(
wp_roles()->role_objects,
function ( WP_Role $role ) use ( $allowed_roles ) {
return ! in_array( $role->name, $allowed_roles, true ) && $role->has_cap( 'advanced_ads_edit_ads' );
}
);
$query_args['role__in'] = wp_list_pluck( $user_roles_to_display, 'name' );
// Exclude super-admins from the author dropdown.
$query_args['exclude'] = array_map(
function ( $login ) {
return get_user_by( 'login', $login )->ID;
},
get_super_admins()
);
return $query_args;
}
/**
* Prevent users from editing the form data and assign ads to users they're not allowed to.
* Wp_die() if tampering detected.
*
* @param int $post_id The current post id.
* @param array $data The post data to be saved.
*
* @return void
*/
public function sanitize_author_saving( $post_id, $data ) {
if (
get_post_type( $post_id ) !== Constants::POST_TYPE_AD ||
get_current_user_id() === (int) $data['post_author'] ||
(int) get_post_field( 'post_author', $post_id ) === (int) $data['post_author']
) {
return;
}
$user_query = new WP_User_Query( $this->filter_ad_authors( [ 'fields' => 'ID' ] ) );
$user_query = array_map( 'absint', $user_query->get_results() );
if ( ! in_array( (int) $data['post_author'], $user_query, true ) ) {
wp_die( esc_html__( 'Sorry, you\'re not allowed to assign this user.', 'advanced-ads' ) );
}
}
/**
* Prevent users from editing posts of users with more rights than themselves.
*
* @param array $caps Needed capabilities.
* @param string $cap Requested capability.
* @param int $user_id The user_id for the cap check.
* @param array $args Arguments array for checking primitive capabilities.
*
* @return array
*/
public function filter_editable_posts( $caps, $cap, $user_id, $args ) {
if ( 'advanced_ads_edit_ads' !== $cap || empty( $args ) ) {
return $caps;
}
$post_id = (int) $args[0];
if ( empty( $post_id ) ) {
return $caps;
}
$ad = wp_advads_get_ad( $post_id );
if ( $ad && ! $ad->is_type( 'plain' ) ) {
return $caps;
}
$author_id = (int) get_post_field( 'post_author', $post_id );
$author = get_userdata( $author_id );
if ( false === $author ) {
$author_id = $user_id;
}
if ( $author_id !== $user_id && ! user_can( $author, $cap, $post_id ) ) {
return [ 'do_not_allow' ];
}
static $users;
if ( null === $users ) {
$user_query = new WP_User_Query( $this->filter_ad_authors( [ 'fields' => 'ID' ] ) );
$users = array_map( 'absint', $user_query->get_results() );
}
if ( ! in_array( $author_id, $users, true ) ) {
return [ 'do_not_allow' ];
}
return $caps;
}
/**
* Get the user roles that are allowed to edit ads.
*
* @return array
*/
private function filtered_user_roles(): array {
$current_user_has_unfiltered_html = current_user_can( 'unfiltered_html' );
return array_filter(
wp_roles()->role_objects,
function ( \WP_Role $role ) use ( $current_user_has_unfiltered_html ) {
if ( $current_user_has_unfiltered_html ) {
return $role->has_cap( 'advanced_ads_edit_ads' );
}
return ! $role->has_cap( 'unfiltered_html' ) && $role->has_cap( 'advanced_ads_edit_ads' );
}
);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* Admin Compatibility.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Compatibility.
*/
class Compatibility implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'quads_meta_box_post_types', [ $this, 'fix_wpquadspro_issue' ], 11 );
}
/**
* Fixes a WP QUADS PRO compatibility issue
* they inject their ad optimization meta box into our ad page, even though it is not a public post type
* using they filter, we remove AA from the list of post types they inject this box into
*
* @param array $allowed_post_types Array of allowed post types.
*
* @return array
*/
public function fix_wpquadspro_issue( $allowed_post_types ): array {
unset( $allowed_post_types['advanced_ads'] );
return $allowed_post_types;
}
}

View File

@@ -0,0 +1,758 @@
<?php // phpcs:ignoreFile
/**
* Allows plugins to use their own update API.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
defined( 'ABSPATH' ) || exit;
/**
* Class Updater
*/
class EDD_Updater {
/**
* The URL pointing to the custom API endpoint.
*
* @var string
*/
private $api_url = '';
/**
* Optional data to send with API calls.
*
* @var array
*/
private $api_data = [];
/**
* Path to the plugin file.
*
* @var string
*/
private $plugin_file = '';
/**
* The plugin basename.
*
* @var string
*/
private $name = '';
/**
* The plugin slug.
*
* @var string
*/
private $slug = '';
/**
* The plugin version.
*
* @var string
*/
private $version = '';
/**
* Whether to override the WordPress.org update check.
*
* @var bool
*/
private $wp_override = false;
/**
* Whether to check for beta versions.
*
* @var bool
*/
private $beta = false;
/**
* The cache key for failed requests.
*
* @var string
*/
private $failed_request_cache_key;
/**
* Class constructor.
*
* @uses plugin_basename()
* @uses hook()
*
* @param string $_api_url The URL pointing to the custom API endpoint.
* @param string $_plugin_file Path to the plugin file.
* @param array $_api_data Optional data to send with API calls.
*/
public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
global $edd_plugin_data;
$this->api_url = trailingslashit( $_api_url );
$this->api_data = $_api_data;
$this->plugin_file = $_plugin_file;
$this->name = plugin_basename( $_plugin_file );
$this->slug = basename( dirname( $_plugin_file ) );
$this->version = $_api_data['version'];
$this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
$this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
$this->failed_request_cache_key = 'edd_sl_failed_http_' . md5( $this->api_url );
$edd_plugin_data[ $this->slug ] = $this->api_data;
/**
* Fires after the $edd_plugin_data is setup.
*
* @since x.x.x
*
* @param array $edd_plugin_data Array of EDD SL plugin data.
*/
do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
// Set up hooks.
$this->init();
}
/**
* Set up WordPress filters to hook into WP's update process.
*
* @uses add_filter()
*
* @return void
*/
public function init() {
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] );
add_filter( 'plugins_api', [ $this, 'plugins_api_filter' ], 10, 3 );
add_action( 'after_plugin_row', [ $this, 'show_update_notification' ], 10, 2 );
add_action( 'admin_init', [ $this, 'show_changelog' ] );
}
/**
* Check for Updates at the defined API endpoint and modify the update array.
*
* This function dives into the update API just when WordPress creates its update array,
* then adds a custom API call and injects the custom plugin data retrieved from the API.
* It is reassembled from parts of the native WordPress plugin update code.
* See wp-includes/update.php line 121 for the original wp_update_plugins() function.
*
* @uses api_request()
*
* @param array $_transient_data Update array build by WordPress.
* @return array Modified update array with custom plugin data.
*/
public function check_update( $_transient_data ) {
if ( ! is_object( $_transient_data ) ) {
$_transient_data = new \stdClass();
}
if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
return $_transient_data;
}
$current = $this->get_update_transient_data();
if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
if ( version_compare( $this->version, $current->new_version, '<' ) ) {
$_transient_data->response[ $this->name ] = $current;
} else {
// Populating the no_update information is required to support auto-updates in WordPress 5.5.
$_transient_data->no_update[ $this->name ] = $current;
}
}
$_transient_data->last_checked = time();
$_transient_data->checked[ $this->name ] = $this->version;
return $_transient_data;
}
/**
* Get repo API data from store.
* Save to cache.
*
* @return \stdClass
*/
public function get_repo_api_data() {
$version_info = $this->get_cached_version_info();
if ( false === $version_info ) {
$version_info = $this->api_request(
'plugin_latest_version',
[
'slug' => $this->slug,
'beta' => $this->beta,
]
);
if ( ! $version_info ) {
return false;
}
// This is required for your plugin to support auto-updates in WordPress 5.5.
$version_info->plugin = $this->name;
$version_info->id = $this->name;
$version_info->tested = $this->get_tested_version( $version_info );
if ( ! isset( $version_info->requires ) ) {
$version_info->requires = '';
}
if ( ! isset( $version_info->requires_php ) ) {
$version_info->requires_php = '';
}
$this->set_version_info_cache( $version_info );
}
return $version_info;
}
/**
* Gets a limited set of data from the API response.
* This is used for the update_plugins transient.
*
* @since 3.8.12
* @return \stdClass|false
*/
private function get_update_transient_data() {
$version_info = $this->get_repo_api_data();
if ( ! $version_info ) {
return false;
}
$limited_data = new \stdClass();
$limited_data->slug = $this->slug;
$limited_data->plugin = $this->name;
$limited_data->url = $version_info->url ?? '';
$limited_data->package = $version_info->package;
$limited_data->icons = $this->convert_object_to_array( $version_info->icons );
$limited_data->banners = $this->convert_object_to_array( $version_info->banners );
$limited_data->new_version = $version_info->new_version;
$limited_data->tested = $version_info->tested ?? '6.7';
$limited_data->requires = $version_info->requires ?? '5.7';
$limited_data->requires_php = $version_info->requires_php ?? '7.4';
return $limited_data;
}
/**
* Gets the plugin's tested version.
*
* @since 1.9.2
* @param object $version_info The version info object.
* @return null|string
*/
private function get_tested_version( $version_info ) {
// There is no tested version.
if ( empty( $version_info->tested ) ) {
return null;
}
// Strip off extra version data so the result is x.y or x.y.z.
list( $current_wp_version ) = explode( '-', get_bloginfo( 'version' ) );
// The tested version is greater than or equal to the current WP version, no need to do anything.
if ( version_compare( $version_info->tested, $current_wp_version, '>=' ) ) {
return $version_info->tested;
}
$current_version_parts = explode( '.', $current_wp_version );
$tested_parts = explode( '.', $version_info->tested );
// The current WordPress version is x.y.z, so update the tested version to match it.
if ( isset( $current_version_parts[2] ) && $current_version_parts[0] === $tested_parts[0] && $current_version_parts[1] === $tested_parts[1] ) {
$tested_parts[2] = $current_version_parts[2];
}
return implode( '.', $tested_parts );
}
/**
* Show the update notification on multisite subsites.
*
* @param string $file The plugin file.
* @param array $plugin The plugin data.
*/
public function show_update_notification( $file, $plugin ) {
// Return early if in the network admin, or if this is not a multisite install.
if ( is_network_admin() || ! is_multisite() ) {
return;
}
// Allow single site admins to see that an update is available.
if ( ! current_user_can( 'activate_plugins' ) ) {
return;
}
if ( $this->name !== $file ) {
return;
}
// Do not print any message if update does not exist.
$update_cache = get_site_transient( 'update_plugins' );
if ( ! isset( $update_cache->response[ $this->name ] ) ) {
if ( ! is_object( $update_cache ) ) {
$update_cache = new \stdClass();
}
$update_cache->response[ $this->name ] = $this->get_repo_api_data();
}
// Return early if this plugin isn't in the transient->response or if the site is running the current or newer version of the plugin.
if ( empty( $update_cache->response[ $this->name ] ) || version_compare( $this->version, $update_cache->response[ $this->name ]->new_version, '>=' ) ) {
return;
}
printf(
'<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
$this->slug, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$file, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
);
echo '<td colspan="3" class="plugin-update colspanchange">';
echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
$changelog_link = '';
if ( ! empty( $update_cache->response[ $this->name ]->sections->changelog ) ) {
$changelog_link = add_query_arg(
[
'edd_sl_action' => 'view_plugin_changelog',
'plugin' => rawurlencode( $this->name ),
'slug' => rawurlencode( $this->slug ),
'TB_iframe' => 'true',
'width' => 77,
'height' => 911,
],
self_admin_url( 'index.php' )
);
}
$update_link = add_query_arg(
[
'action' => 'upgrade-plugin',
'plugin' => rawurlencode( $this->name ),
],
self_admin_url( 'update.php' )
);
printf(
/* translators: the plugin name. */
esc_html__( 'There is a new version of %1$s available.', 'advanced-ads' ),
esc_html( $plugin['Name'] )
);
if ( ! current_user_can( 'update_plugins' ) ) {
echo ' ';
esc_html_e( 'Contact your network administrator to install the update.', 'advanced-ads' );
} elseif ( empty( $update_cache->response[ $this->name ]->package ) && ! empty( $changelog_link ) ) {
echo ' ';
printf(
/* translators: 1: opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
__( '%1$sView version %2$s details%3$s.', 'advanced-ads' ), // phpcs:ignore
'<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
esc_html( $update_cache->response[ $this->name ]->new_version ),
'</a>'
);
} elseif ( ! empty( $changelog_link ) ) {
echo ' ';
printf(
__( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'advanced-ads' ), // phpcs:ignore
'<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
esc_html( $update_cache->response[ $this->name ]->new_version ),
'</a>',
'<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
'</a>'
);
} else {
printf(
' %1$s%2$s%3$s',
'<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
esc_html__( 'Update now.', 'advanced-ads' ),
'</a>'
);
}
do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
echo '</p></div></td></tr>';
}
/**
* Gets the plugins active in a multisite network.
*
* @return array
*/
private function get_active_plugins() {
$active_plugins = (array) get_option( 'active_plugins' );
$active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
}
/**
* Updates information on the "View version x.x details" page with custom data.
*
* @uses api_request()
*
* @param mixed $_data The data originally requested.
* @param string $_action The type of information being requested.
* @param object $_args Plugin API arguments.
* @return object $_data
*/
public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
if ( 'plugin_information' !== $_action ) {
return $_data;
}
if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
return $_data;
}
$to_send = [
'slug' => $this->slug,
'is_ssl' => is_ssl(),
'fields' => [
'banners' => [],
'reviews' => false,
'icons' => [],
],
];
// Get the transient where we store the api request for this plugin for 24 hours.
$edd_api_request_transient = $this->get_cached_version_info();
// If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
if ( empty( $edd_api_request_transient ) ) {
$api_response = $this->api_request( 'plugin_information', $to_send );
// Expires in 3 hours.
$this->set_version_info_cache( $api_response );
if ( false !== $api_response ) {
$_data = $api_response;
}
} else {
$_data = $edd_api_request_transient;
}
// Convert sections into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
$_data->sections = $this->convert_object_to_array( $_data->sections );
}
// Convert banners into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
$_data->banners = $this->convert_object_to_array( $_data->banners );
}
// Convert icons into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
$_data->icons = $this->convert_object_to_array( $_data->icons );
}
// Convert contributors into an associative array, since we're getting an object, but Core expects an array.
if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
$_data->contributors = $this->convert_object_to_array( $_data->contributors );
}
if ( ! isset( $_data->plugin ) ) {
$_data->plugin = $this->name;
}
if ( ! isset( $_data->version ) && ! empty( $_data->new_version ) ) {
$_data->version = $_data->new_version;
}
return $_data;
}
/**
* Convert some objects to arrays when injecting data into the update API
*
* Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
* decoding, they are objects. This method allows us to pass in the object and return an associative array.
*
* @since 3.6.5
*
* @param \stdClass $data The object to convert to an array.
*
* @return array
*/
private function convert_object_to_array( $data ) {
if ( ! is_array( $data ) && ! is_object( $data ) ) {
return [];
}
$new_data = [];
foreach ( $data as $key => $value ) {
$new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
}
return $new_data;
}
/**
* Disable SSL verification in order to prevent download update failures
*
* @param array $args Array of HTTP request arguments.
* @param string $url The request URL.
* @return object $array
*/
public function http_request_args( $args, $url ) {
if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
$args['sslverify'] = $this->verify_ssl();
}
return $args;
}
/**
* Calls the API and, if successfull, returns the object delivered by the API.
*
* @uses get_bloginfo()
* @uses wp_remote_post()
* @uses is_wp_error()
*
* @param string $_action The requested action.
* @param array $_data Parameters for the API action.
* @return false|object|void
*/
private function api_request( $_action, $_data ) {
$data = array_merge( $this->api_data, $_data );
if ( $data['slug'] !== $this->slug ) {
return;
}
// Don't allow a plugin to ping itself.
if ( trailingslashit( home_url() ) === $this->api_url ) {
return false;
}
if ( $this->request_recently_failed() ) {
return false;
}
return $this->get_version_from_remote();
}
/**
* Determines if a request has recently failed.
*
* @since 1.9.1
*
* @return bool
*/
private function request_recently_failed() {
$failed_request_details = get_option( $this->failed_request_cache_key );
// Request has never failed.
if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
return false;
}
/*
* Request previously failed, but the timeout has expired.
* This means we're allowed to try again.
*/
if ( time() > $failed_request_details ) {
delete_option( $this->failed_request_cache_key );
return false;
}
return true;
}
/**
* Logs a failed HTTP request for this API URL.
* We set a timestamp for 1 hour from now. This prevents future API requests from being
* made to this domain for 1 hour. Once the timestamp is in the past, API requests
* will be allowed again. This way if the site is down for some reason we don't bombard
* it with failed API requests.
*
* @see EDD_SL_Plugin_Updater::request_recently_failed
*
* @since 1.9.1
*/
private function log_failed_request() {
update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
}
/**
* If available, show the changelog for sites in a multisite install.
*/
public function show_changelog() {
if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
return;
}
if ( empty( $_REQUEST['plugin'] ) ) {
return;
}
if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
return;
}
if ( ! current_user_can( 'update_plugins' ) ) {
wp_die( esc_html__( 'You do not have permission to install plugin updates', 'advanced-ads' ), esc_html__( 'Error', 'advanced-ads' ), [ 'response' => 403 ] );
}
$version_info = $this->get_repo_api_data();
if ( isset( $version_info->sections ) ) {
$sections = $this->convert_object_to_array( $version_info->sections );
if ( ! empty( $sections['changelog'] ) ) {
echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
}
}
exit;
}
/**
* Gets the current version information from the remote site.
*
* @return array|false
*/
private function get_version_from_remote() {
$api_params = [
'edd_action' => 'get_version',
'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
'slug' => $this->slug,
'author' => $this->api_data['author'],
'url' => home_url(),
'beta' => $this->beta,
'php_version' => phpversion(),
'wp_version' => get_bloginfo( 'version' ),
];
/**
* Filters the parameters sent in the API request.
*
* @param array $api_params The array of data sent in the request.
* @param array $this->api_data The array of data set up in the class constructor.
* @param string $this->plugin_file The full path and filename of the file.
*/
$api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
$request = wp_remote_post(
$this->api_url,
[
'timeout' => 15,
'sslverify' => $this->verify_ssl(),
'body' => $api_params,
]
);
if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
$this->log_failed_request();
return false;
}
$body = json_decode( wp_remote_retrieve_body( $request ) );
if ( $body && isset( $body->sections ) ) {
$body->sections = maybe_unserialize( $body->sections );
} else {
$body = false;
}
if ( $body && isset( $body->banners ) ) {
$body->banners = maybe_unserialize( $body->banners );
}
if ( $body && isset( $body->icons ) ) {
$body->icons = maybe_unserialize( $body->icons );
}
if ( ! empty( $body->sections ) ) {
foreach ( $body->sections as $key => $section ) {
$body->$key = (array) $section;
}
}
return $body;
}
/**
* Get the version info from the cache, if it exists.
*
* @param string $cache_key The cache key to use.
* @return object
*/
public function get_cached_version_info( $cache_key = '' ) {
if ( empty( $cache_key ) ) {
$cache_key = $this->get_cache_key();
}
$cache = get_option( $cache_key );
// Cache is expired.
if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
return false;
}
// We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
$cache['value'] = json_decode( $cache['value'] );
if ( ! empty( $cache['value']->icons ) ) {
$cache['value']->icons = (array) $cache['value']->icons;
}
return $cache['value'];
}
/**
* Adds the plugin version information to the database.
*
* @param string $value The value to store.
* @param string $cache_key The cache key to use.
*/
public function set_version_info_cache( $value = '', $cache_key = '' ) {
if ( empty( $cache_key ) ) {
$cache_key = $this->get_cache_key();
}
$data = [
'timeout' => strtotime( '+12 hours', time() ),
'value' => wp_json_encode( $value ),
];
update_option( $cache_key, $data, 'no' );
// Delete the duplicate option.
delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
}
/**
* Returns if the SSL of the store should be verified.
*
* @since 1.6.13
* @return bool
*/
private function verify_ssl() {
return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
}
/**
* Gets the unique key (option name) for a plugin.
*
* @since 1.9.0
* @return string
*/
private function get_cache_key() {
$string = $this->slug . $this->api_data['license'] . $this->beta;
return 'advads_edd_sl_' . md5( serialize( $string ) );
}
}

View File

@@ -0,0 +1,480 @@
<?php
/**
* Admin Groups List Table.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use WP_Meta_Query;
use WP_Term_Query;
use AdvancedAds\Modal;
use WP_Terms_List_Table;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Utilities\Params;
defined( 'ABSPATH' ) || exit;
/**
* Admin Groups List Table.
*/
class Groups_List_Table extends WP_Terms_List_Table {
/**
* Missing type error.
*
* @var string
*/
private $type_error = '';
/**
* Array with all ads.
*
* @var $all_ads
*/
private $all_ads = [];
/**
* Array of group ads info.
*
* @var array
*/
private $group_ads_info = [];
/**
* Hints html.
*
* @var string
*/
private $hints_html = null;
/**
* Construct the current list
*/
public function __construct() {
parent::__construct();
$this->prepare_items();
add_action( 'pre_get_terms', [ $this, 'sorting_query' ] );
add_filter( 'default_hidden_columns', [ $this, 'default_hidden_columns' ] );
}
/**
* Modify sorting query
*
* @param WP_Term_Query $query Query object.
*
* @return WP_Term_Query
*/
public function sorting_query( $query ) {
switch ( $query->query_vars['orderby'] ) {
case 'date':
// phpcs:ignore Generic.Formatting.MultipleStatementAlignment.NotSameWarning
$meta_query_args = [
'relation' => 'OR',
[
'key' => 'modified_date',
'compare' => 'EXISTS',
],
[
'key' => 'modified_date',
'compare' => 'NOT EXISTS',
],
]; // include all groups with and without a modified date so empty date groups are shown.
$meta_query = new WP_Meta_Query( $meta_query_args );
$query->meta_query = $meta_query;
$query->query_vars['orderby'] = 'meta_value';
break;
case 'details':
$query->query_vars['orderby'] = 'term_id';
break;
}
return $query;
}
/**
* Load groups
*
* @return void
*/
public function prepare_items(): void {
parent::prepare_items();
$args = $this->callback_args;
$args['taxonomy'] = $this->screen->taxonomy;
$args['hide_empty'] = 0;
$args['offset'] = ( $args['page'] - 1 ) * $args['number'];
// Save the values because 'number' and 'offset' can be subsequently overridden.
$this->callback_args = $args;
if ( is_taxonomy_hierarchical( $args['taxonomy'] ) && ! isset( $args['orderby'] ) ) {
// We'll need the full set of terms then.
$args['number'] = 0;
$args['offset'] = $args['number'];
}
$args = $this->query_filters( $args );
$this->items = get_terms( $args );
$this->set_pagination_args(
[
'total_items' => wp_count_terms(
[
'taxonomy' => $args['taxonomy'],
'search' => $args['search'],
]
),
'per_page' => $args['number'],
]
);
$this->all_ads = wp_advads_get_ads_dropdown();
$this->items = array_map(
function ( $term_id ) {
return wp_advads_get_group( $term_id );
},
$this->items ?? []
);
}
/**
* Gets the number of items to display on a single page.
*
* @param string $option User option name.
* @param int $default_value The number of items to display.
*
* @return int
*/
protected function get_items_per_page( $option, $default_value = 20 ): int {
return 10000;
}
/**
* Gets a list of CSS classes for the WP_List_Table table tag.
*
* @return array
*/
protected function get_table_classes(): array {
return array_merge( parent::get_table_classes(), [ 'advads-table' ] );
}
/**
* Renders filters
*
* @return void
*/
public function render_filters(): void {
include ADVADS_ABSPATH . 'views/admin/tables/groups/filters.php';
}
/**
* No groups found
*
* @return void
*/
public function no_items(): void {
esc_html_e( 'No Ad Group found', 'advanced-ads' );
}
/**
* Get columns
*
* @return array
*/
public function get_columns(): array {
return [
'type' => __( 'Type', 'advanced-ads' ),
'name' => _x( 'Name', 'term name', 'advanced-ads' ),
'details' => __( 'Details', 'advanced-ads' ),
'ads' => __( 'Ads', 'advanced-ads' ),
'date' => __( 'Date', 'advanced-ads' ),
];
}
/**
* Hidden columns
*
* @param string[] $hidden Column list.
*
* @return array
*/
public function default_hidden_columns( $hidden ): array {
$hidden[] = 'date';
return $hidden;
}
/**
* Sortable columns
*
* @return array
*/
public function get_sortable_columns(): array {
return [
'date' => 'date',
'name' => 'name',
'details' => 'details',
];
}
/**
* Displays the table.
*
* @return void
*/
public function display(): void {
$singular = $this->_args['singular'];
$this->screen->render_screen_reader_content( 'heading_list' );
?>
<table class="<?php echo esc_attr( implode( ' ', $this->get_table_classes() ) ); ?>">
<?php $this->print_table_description(); ?>
<thead>
<tr>
<?php $this->print_column_headers(); ?>
</tr>
</thead>
<tbody id="the-list"
<?php
if ( $singular ) {
echo " data-wp-lists='list:$singular'"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
>
<?php $this->display_rows_or_placeholder(); ?>
</tbody>
</table>
<?php
}
/**
* Display rows or placeholder
*
* @return void
*/
public function display_rows_or_placeholder(): void {
if ( empty( $this->items ) || ! is_array( $this->items ) ) {
echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$this->no_items();
echo '</td></tr>';
return;
}
foreach ( $this->items as $term ) {
$this->single_row( $term );
}
}
/**
* Render single row.
*
* @param Group $group Term object.
* @param int $level Depth level.
*
* @return void
*/
public function single_row( $group, $level = 0 ): void {
$this->type_error = '';
// Set the group to behave as default, if the original type is not available.
$group_type = $group->get_type_object();
if ( $group_type->is_premium() ) {
$this->type_error = sprintf(
/* translators: %s is the group type string */
__( 'The originally selected group type “%s” is not enabled.', 'advanced-ads' ),
$group_type->get_title()
);
}
echo '<tr id="tag-' . $group->get_id() . '" class="' . $level . '">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$this->single_row_columns( $group );
echo '</tr>';
}
/**
* Column type
*
* @param Group $group Group instance.
*
* @return void
*/
public function column_type( $group ): void {
include ADVADS_ABSPATH . 'views/admin/tables/groups/column-type.php';
}
/**
* Column name
*
* @param Group $group Group instance.
*
* @return void
*/
public function column_name( $group ): void {
$this->render_edit_modal( $group );
$this->render_usage_modal( $group );
include ADVADS_ABSPATH . 'views/admin/tables/groups/column-name.php';
}
/**
* Column details
*
* @param Group $group Group instance.
*
* @return void
*/
public function column_details( $group ): void {
include ADVADS_ABSPATH . 'views/admin/tables/groups/column-details.php';
}
/**
* Column ads
*
* @param Group $group Group instance.
*
* @return void
*/
public function column_ads( $group ): void {
$template = empty( $group->get_ad_weights() ) ? 'list-row-loop-none.php' : 'list-row-loop.php';
include ADVADS_ABSPATH . 'views/admin/tables/groups/' . $template;
}
/**
* Column date
*
* @param Group $group Group instance.
*
* @return void
*/
public function column_date( $group ) {
$publish_date = $group->get_publish_date();
$modified_date = $group->get_modified_date();
if ( ! $publish_date && ! $modified_date ) {
return;
}
$date_time_regex = get_option( 'date_format' ) . ' \\a\\t ' . get_option( 'time_format' );
$date_prefix = $publish_date === $modified_date ? __( 'Published', 'advanced-ads' ) : __( 'Last Modified', 'advanced-ads' );
$date_to_show = get_date_from_gmt( $publish_date === $modified_date ? $publish_date : $modified_date, $date_time_regex );
echo esc_html( $date_prefix ) . '<br>' . esc_html( $date_to_show );
}
/**
* Generates and displays row action links.
*
* @param Group $group Group instance.
* @param string $column_name Column name.
* @param string $primary Primary column name.
*
* @return string
*/
protected function handle_row_actions( $group, $column_name, $primary ): string {
global $tax;
if ( $primary !== $column_name ) {
return '';
}
$actions = [];
if ( ! $this->type_error && current_user_can( $tax->cap->edit_terms ) ) {
// edit group link.
$actions['edit'] = '<a href="#modal-group-edit-' . $group->get_id() . '"
class="edits">' . esc_html__( 'Edit', 'advanced-ads' ) . '</a>';
// duplicate group upgrade link.
if ( ! defined( 'AAP_VERSION' ) ) {
$actions['duplicate-group'] = ( new Upgrades() )->create_duplicate_link();
}
}
$actions['usage'] = '<a href="#modal-group-usage-' . $group->get_id() . '" class="edits">' . esc_html__( 'Show Usage', 'advanced-ads' ) . '</a>';
if ( current_user_can( $tax->cap->delete_terms ) ) {
$args = [
'action' => 'group',
'action2' => 'delete',
'group_id' => $group->get_id(),
'page' => 'advanced-ads-groups',
];
$delete_link = add_query_arg( $args, admin_url( 'admin.php' ) );
$actions['delete'] = "<a class='delete-tag' href='" . wp_nonce_url( $delete_link, 'delete-tag_' . $group->get_id() ) . "'>" . __( 'Delete', 'advanced-ads' ) . '</a>';
}
$actions = apply_filters( Constants::TAXONOMY_GROUP . '_row_actions', $actions, $group );
return $this->row_actions( $actions );
}
/**
* Render edit form modal
*
* @param Group $group Group instance.
*
* @return void
*/
private function render_edit_modal( $group ): void {
ob_start();
require ADVADS_ABSPATH . 'views/admin/tables/groups/edit-form-modal.php';
$modal_content = ob_get_clean();
Modal::create(
[
'modal_slug' => 'group-edit-' . $group->get_id(),
'modal_content' => $modal_content,
'modal_title' => sprintf( '%s %s', __( 'Edit', 'advanced-ads' ), $group->get_name() ),
]
);
}
/**
* Render usage form modal
*
* @param Group $group Group instance.
*
* @return void
*/
private function render_usage_modal( $group ): void {
ob_start();
include ADVADS_ABSPATH . 'views/admin/tables/groups/column-usage.php';
$modal_content = ob_get_clean();
Modal::create(
[
'modal_slug' => 'group-usage-' . $group->get_id(),
'modal_content' => $modal_content,
'modal_title' => __( 'Usage', 'advanced-ads' ),
'cancel_action' => false,
'close_action' => __( 'Close', 'advanced-ads' ),
]
);
}
/**
* Check filters before loading group.
*
* @param array $args The arguments array for the groups list table.
*
* @return array The modified arguments array with the added meta query.
*/
private function query_filters( $args ): array {
$group_type = Params::get( 'group_type' );
if ( $group_type ) {
$args['meta_query'] = [ // phpcs:ignore
[
'key' => '_advads_group_type',
'value' => $group_type,
'compare' => '=',
],
];
}
return $args;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* The class is responsible for rendering a branded header on plugin pages in the WordPress admin area.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Entities;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Header.
*/
class Header implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'in_admin_header', [ $this, 'render' ] );
}
/**
* Add an Advanced Ads branded header to plugin pages
*
* @return void
*/
public function render(): void {
// Early bail!!
if ( ! Conditional::is_screen_advanced_ads() ) {
return;
}
$screen = get_current_screen();
$manual_url = 'https://wpadvancedads.com/manual/';
$new_button_id = '';
$new_button_label = '';
$new_button_href = '';
$show_filter_button = false;
$reset_href = '';
$filter_disabled = $screen->get_option( 'show-filters' ) ? 'disabled' : '';
$show_screen_options = false;
$title = get_admin_page_title();
$tooltip = '';
switch ( $screen->id ) {
case 'advanced_ads':
$new_button_label = __( 'New Ad', 'advanced-ads' );
$new_button_href = admin_url( 'post-new.php?post_type=advanced_ads' );
$manual_url = 'https://wpadvancedads.com/manual/first-ad/';
break;
case 'edit-advanced_ads':
$title = __( 'Your Ads', 'advanced-ads' );
$new_button_label = __( 'New Ad', 'advanced-ads' );
$new_button_href = admin_url( 'post-new.php?post_type=advanced_ads' );
$manual_url = 'https://wpadvancedads.com/manual/first-ad/';
$show_filter_button = ! Conditional::has_filter_or_search();
$reset_href = ! $show_filter_button ? esc_url( admin_url( 'edit.php?post_type=' . Constants::POST_TYPE_AD ) ) : '';
$show_screen_options = true;
break;
case 'advanced-ads_page_advanced-ads-groups':
$title = __( 'Your Groups', 'advanced-ads' );
$new_button_label = __( 'New Ad Group', 'advanced-ads' );
$new_button_href = '#modal-group-new';
$new_button_id = 'advads-new-ad-group-link';
$manual_url = 'https://wpadvancedads.com/manual/ad-groups/';
$show_filter_button = ! Conditional::has_filter_or_search();
$reset_href = ! $show_filter_button ? esc_url( admin_url( 'admin.php?page=advanced-ads-groups' ) ) : '';
$show_screen_options = true;
$tooltip = Entities::get_group_description();
break;
case 'advanced-ads_page_advanced-ads-placements':
$title = __( 'Your Placements', 'advanced-ads' );
$new_button_label = __( 'New Placement', 'advanced-ads' );
$new_button_href = '#modal-placement-new';
$manual_url = 'https://wpadvancedads.com/manual/placements/';
$show_filter_button = true;
$tooltip = Entities::get_placement_description();
break;
case 'edit-advanced_ads_plcmnt':
$title = __( 'Your Placements', 'advanced-ads' );
$new_button_label = __( 'New Placement', 'advanced-ads' );
$new_button_href = '#modal-placement-new';
$manual_url = 'https://wpadvancedads.com/manual/placements/';
$show_filter_button = ! Conditional::has_filter_or_search();
$reset_href = ! $show_filter_button ? esc_url( admin_url( 'edit.php?post_type=' . Constants::POST_TYPE_PLACEMENT ) ) : '';
$show_screen_options = true;
$tooltip = Entities::get_placement_description();
break;
case 'advanced-ads_page_advanced-ads-settings':
$title = __( 'Advanced Ads Settings', 'advanced-ads' );
break;
}
$manual_url = apply_filters( 'advanced-ads-admin-header-manual-url', $manual_url, $screen->id );
include ADVADS_ABSPATH . 'views/admin/header.php';
}
}

View File

@@ -0,0 +1,551 @@
<?php
/**
* Container class for custom filters on admin ad list page.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.x.x
*/
namespace AdvancedAds\Admin;
use WP_Post;
use Exception;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Class List_Filters
*/
class List_Filters implements Integration_Interface {
/**
* Ads data for the ad list table
*
* @var array
*/
protected $all_ads = [];
/**
* Ads ad groups
*
* @var array
*/
protected $all_groups = [];
/**
* Ads in each group
*
* @var array
*/
protected $ads_in_groups = [];
/**
* All filters available in the current ad list table
*
* @var array
*/
protected $all_filters = [];
/**
* All ad options for the ad list table
*
* @var array
*/
protected $all_ads_options = [];
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'posts_results', [ $this, 'post_results' ], 10, 2 );
add_filter( 'post_limits', [ $this, 'limit_filter' ], 10, 2 );
}
/**
* Collect available filters for ad overview page.
*
* @param array $posts array of ads.
*
* @return void
*/
private function collect_filters( $posts ): void {
$all_filters = [
'all_sizes' => [],
'all_types' => [],
'all_dates' => [],
'all_groups' => [],
'all_authors' => $this->collect_authors(),
];
// Can not filter correctly with "trashed" posts. Do not display any filtering option in this case.
if ( Params::request( 'post_status' ) === 'trash' ) {
$this->all_filters = $all_filters;
return;
}
$groups_to_check = $this->ads_in_groups;
foreach ( $posts as $post ) {
$ad_option = $this->all_ads_options[ $post->ID ];
foreach ( $groups_to_check as $key => $ads ) {
if ( ! isset( $all_filters['all_groups'][ $key ] ) // skip if this group is already known.
&& isset( $this->all_groups[ $key ] ) ) {
$all_filters['all_groups'][ $key ] = $this->all_groups[ $key ]['name'];
// remove groups that are already selected for the filter to reduce loop items next time.
unset( $groups_to_check[ $key ] );
}
}
if ( isset( $ad_option['width'], $ad_option['height'] ) && $ad_option['width'] && $ad_option['height'] ) {
if ( ! array_key_exists( $ad_option['width'] . 'x' . $ad_option['height'], $all_filters['all_sizes'] ) ) {
$all_filters['all_sizes'][ $ad_option['width'] . 'x' . $ad_option['height'] ] = $ad_option['width'] . ' x ' . $ad_option['height'];
}
}
if ( isset( $ad_option['type'] ) && 'adsense' === $ad_option['type'] ) {
$content = $this->all_ads[ array_search( $post->ID, wp_list_pluck( $this->all_ads, 'ID' ), true ) ]->post_content;
try {
$adsense_obj = json_decode( $content, true );
} catch ( Exception $e ) {
$adsense_obj = false;
}
if ( $adsense_obj ) {
if ( 'responsive' === $adsense_obj['unitType'] ) {
if ( ! array_key_exists( 'responsive', $all_filters['all_sizes'] ) ) {
$all_filters['all_sizes']['responsive'] = __( 'Responsive', 'advanced-ads' );
}
}
}
}
if (
isset( $ad_option['type'] ) // could be missing for new ads that are stored only by WP auto-save.
&& ! array_key_exists( $ad_option['type'], $all_filters['all_types'] )
&& wp_advads_has_ad_type( $ad_option['type'] )
) {
$all_filters['all_types'][ $ad_option['type'] ] = wp_advads_get_ad_type( $ad_option['type'] )->get_title();
}
$all_filters = apply_filters( 'advanced-ads-ad-list-column-filter', $all_filters, $post, $ad_option );
}
$this->all_filters = $all_filters;
}
/**
* Collects all ads data.
*
* @param WP_Post[] $posts array of ads.
*/
public function collect_all_ads( $posts ) {
foreach ( $posts as $post ) {
$this->all_ads_options[ $post->ID ] = get_post_meta( $post->ID, 'advanced_ads_ad_options', true );
if ( empty( $this->all_ads_options[ $post->ID ] ) ) {
$this->all_ads_options[ $post->ID ] = [];
}
}
$this->all_ads = $posts;
}
/**
* Collects all ads groups, fills the $all_groups class property.
*/
private function collect_all_groups() {
global $wpdb;
$groups = wp_advads_get_all_groups();
foreach ( $groups as $group ) {
$group_id = $group->get_id();
$ad_weights = $group->get_ad_weights();
if ( ! empty( $ad_weights ) ) {
$groups[ $group_id ] = [ 'name' => $group->get_name() ];
foreach ( $ad_weights as $ad_id => $weight ) {
$this->ads_in_groups[ $group_id ][] = $ad_id;
}
}
}
$this->all_groups = $groups;
}
/**
* Retrieve the stored ads list.
*/
public function get_all_ads() {
return $this->all_ads;
}
/**
* Retrieve all filters that can be applied.
*/
public function get_all_filters() {
return $this->all_filters;
}
/**
* Remove limits because we need to get all ads.
*
* @param string $limits The LIMIT clause of the query.
* @param WP_Query $the_query the current WP_Query object.
* @return string $limits The LIMIT clause of the query.
*/
public function limit_filter( $limits, $the_query ) {
// Execute only in the main query.
if ( ! $the_query->is_main_query() ) {
return $limits;
}
if ( ! function_exists( 'get_current_screen' ) ) {
return $limits;
}
$screen = get_current_screen();
// Execute only in the ad list page.
if ( ! $screen || 'edit-advanced_ads' !== $screen->id ) {
return $limits;
}
return '';
}
/**
* Edit the query for list table.
*
* @param array $posts the posts array from the query.
* @param WP_Query $the_query the current WP_Query object.
*
* @return array with posts
*/
public function post_results( $posts, $the_query ): array {
// Execute only in the main query.
if ( ! function_exists( 'get_current_screen' ) || ! $the_query->is_main_query() ) {
return $posts;
}
$screen = get_current_screen();
// Execute only in the ad list page.
if ( ! $screen || 'edit-advanced_ads' !== $screen->id ) {
return $posts;
}
// Searching an ad ID.
if ( 0 !== (int) $the_query->query_vars['s'] ) {
$single_ad = wp_advads_ad_query(
[
'p' => (int) $the_query->query_vars['s'],
'post_status' => [ 'any' ],
]
)->posts;
if ( ! empty( $single_ad ) ) {
// Head to the ad edit page if one and only one ad found.
$redirect = add_query_arg(
[
'post' => $single_ad[0]->ID,
'action' => 'edit',
],
admin_url( 'post.php' )
);
if ( empty( $posts ) && wp_safe_redirect( $redirect ) ) {
exit;
}
if ( ! in_array( $single_ad[0]->ID, wp_list_pluck( $posts, 'ID' ), true ) ) {
$posts[] = $single_ad[0];
}
}
}
$this->collect_all_ads( $posts );
$this->collect_all_groups();
$new_posts = Params::request( 'post_status' ) === 'trash'
? $posts
: $this->ad_filters( $this->all_ads, $the_query );
$per_page = $the_query->query_vars['posts_per_page'] ? $the_query->query_vars['posts_per_page'] : 20;
if ( $per_page < count( $new_posts ) ) {
$paged = Params::request( 'paged', 1, FILTER_VALIDATE_INT );
$total = count( $new_posts );
$new_posts = array_slice( $new_posts, ( $paged - 1 ) * $per_page, $per_page );
$the_query->found_posts = $total;
$the_query->post_count = count( $new_posts );
}
// replace the post list.
$the_query->posts = $new_posts;
return $new_posts;
}
/**
* Apply ad filters on post array
*
* @param array $posts the original post array.
* @param WP_Query $the_query the current WP_Query object.
*
* @return array with posts
*/
private function ad_filters( $posts, &$the_query ) {
global $wpdb;
$using_original = true;
$request = wp_unslash( $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
/**
* Filter post status
*/
if ( isset( $request['post_status'] ) && '' !== $request['post_status'] && ! in_array( $request['post_status'], [ 'all', 'trash' ], true ) ) {
$new_posts = [];
foreach ( $this->all_ads as $post ) {
if ( $request['post_status'] === $post->post_status ) {
$new_posts[] = $post;
}
}
$posts = $new_posts;
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter post author
*/
if ( isset( $request['author'] ) && '' !== $request['author'] ) {
$author = absint( $request['author'] );
$new_posts = [];
$the_list = $using_original ? $this->all_ads : $posts;
foreach ( $the_list as $post ) {
if ( absint( $post->post_author ) === $author ) {
$new_posts[] = $post;
}
}
$posts = $new_posts;
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter groups
*/
if ( isset( $request['adgroup'] ) && '' !== $request['adgroup'] ) {
$new_posts = [];
$the_list = $using_original ? $this->all_ads : $posts;
foreach ( $the_list as $post ) {
if ( isset( $this->ads_in_groups[ absint( $request['adgroup'] ) ] ) &&
in_array( $post->ID, $this->ads_in_groups[ absint( $request['adgroup'] ) ], true ) ) {
$new_posts[] = $post;
}
}
$posts = $new_posts;
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter by taxonomy
*/
if ( isset( $request['taxonomy'] ) && isset( $request['term'] ) ) {
$term = $request['term'];
$query = "SELECT object_id
FROM {$wpdb->term_relationships}
WHERE term_taxonomy_id = (
SELECT terms.term_id
FROM {$wpdb->terms} AS terms
INNER JOIN {$wpdb->term_taxonomy} AS term_taxonomy
ON terms.term_id = term_taxonomy.term_id
WHERE terms.slug = %s AND term_taxonomy.taxonomy = %s
)";
$object_ids = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->prepare( $query, $term, Constants::TAXONOMY_GROUP ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
'ARRAY_A'
);
$ads_in_taxonomy = [];
foreach ( $object_ids as $object ) {
$ads_in_taxonomy[] = absint( $object['object_id'] );
}
$new_posts = [];
$the_list = $using_original ? $this->all_ads : $posts;
foreach ( $the_list as $post ) {
if ( in_array( $post->ID, $ads_in_taxonomy, true ) ) {
$new_posts[] = $post;
}
}
$posts = $new_posts;
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter ad type
*/
if ( isset( $request['adtype'] ) && '' !== $request['adtype'] ) {
$new_posts = [];
$the_list = $using_original ? $this->all_ads : $posts;
foreach ( $the_list as $post ) {
$option = $this->all_ads_options[ $post->ID ];
if ( isset( $option['type'] ) && $request['adtype'] === $option['type'] ) {
$new_posts[] = $post;
}
}
$posts = $new_posts;
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter debug mode
*/
$debugmode = Params::request( 'ad_debugmode', false );
if ( $debugmode ) {
$posts = array_filter(
$using_original ? $this->all_ads : $posts,
function ( $post ) use ( $debugmode ) {
$option = $this->all_ads_options[ $post->ID ]['debugmode'] ?? '';
return ( 'yes' === $debugmode && ! empty( $option ) ) || ( 'no' === $debugmode && empty( $option ) );
}
);
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter displayonce
*/
$displayonce = Params::request( 'ad_displayonce', false );
if ( $displayonce ) {
$posts = array_filter(
$using_original ? $this->all_ads : $posts,
function ( $post ) use ( $displayonce ) {
$option = $this->all_ads_options[ $post->ID ]['once_per_page'] ?? '';
return ( 'yes' === $displayonce && ! empty( $option ) ) || ( 'no' === $displayonce && empty( $option ) );
}
);
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter privacyignore
*/
$privacyignore = Params::request( 'ad_privacyignore', false );
if ( $privacyignore ) {
$posts = array_filter(
$using_original ? $this->all_ads : $posts,
function ( $post ) use ( $privacyignore ) {
$option = $this->all_ads_options[ $post->ID ]['privacy']['ignore-consent'] ?? '';
return ( 'yes' === $privacyignore && ! empty( $option ) ) || ( 'no' === $privacyignore && empty( $option ) );
}
);
$the_query->found_posts = count( $posts );
$using_original = false;
}
/**
* Filter ad size
*/
if ( isset( $request['adsize'] ) && '' !== $request['adsize'] ) {
$new_posts = [];
$the_list = $using_original ? $this->all_ads : $posts;
foreach ( $the_list as $post ) {
$option = $this->all_ads_options[ $post->ID ];
if ( 'responsive' === $request['adsize'] ) {
if ( isset( $option['type'] ) && 'adsense' === $option['type'] ) {
$content = false;
try {
$content = json_decode( $post->post_content, true );
} catch ( Exception $e ) {
$content = false;
}
if ( $content && 'responsive' === $content['unitType'] ) {
$new_posts[] = $post;
}
}
} else {
$width = isset( $option['width'] ) ? $option['width'] : 0;
$height = isset( $option['height'] ) ? $option['height'] : 0;
if ( $request['adsize'] === $width . 'x' . $height ) {
$new_posts[] = $post;
}
}
}
$posts = $new_posts;
$the_query->found_posts = count( $posts );
$using_original = false;
}
if ( isset( $request['addate'] ) ) {
$filter_value = urldecode( $request['addate'] );
if ( in_array( $filter_value, [ 'advads-filter-expired', 'advads-filter-expiring' ], true ) ) {
$posts = $this->filter_expired_ads( $filter_value, $using_original ? $this->all_ads : $posts );
}
}
$posts = apply_filters( 'advanced-ads-ad-list-filter', $posts, $this->all_ads_options );
$the_query->found_posts = count( $posts );
$this->collect_filters( $posts );
return $posts;
}
/**
* Filter by expiring or expired ads.
*
* @param string $filter The current filter name, expired or expiring.
* @param WP_Post[] $posts The array of posts.
*
* @return WP_Post[]
*/
private function filter_expired_ads( $filter, $posts ) {
$now = time();
return array_filter(
$posts,
function ( WP_Post $post ) use ( $now, $filter ) {
$option = $this->all_ads_options[ $post->ID ];
if ( empty( $option['expiry_date'] ) ) {
return false;
}
$is_expired = 'advads-filter-expired' === $filter && $option['expiry_date'] <= $now;
$in_future = 'advads-filter-expiring' === $filter && $option['expiry_date'] > $now;
return $is_expired || $in_future;
}
);
}
/**
* Author filter dropdown data.
*
* @return array An associative array of authors, keys are the author IDs and values are the author display names.
*/
private function collect_authors(): array {
$ads = wp_advads_get_all_ads();
$authors = [];
foreach ( $ads as $ad ) {
if ( ! isset( $authors[ $ad->get_author_id() ] ) ) {
$authors[ $ad->get_author_id() ] = get_the_author_meta( 'display_name', $ad->get_author_id() );
}
}
return $authors;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* The class is responsible for adding marketing widgets to the plugin.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Marketing.
*/
class Marketing implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'add_meta_boxes_' . Constants::POST_TYPE_AD, [ $this, 'add_meta_boxes' ] );
}
/**
* Add meta boxes
*
* @return void
*/
public function add_meta_boxes(): void {
if ( ! defined( 'AAP_VERSION' ) ) {
add_meta_box(
'advads-pro-pitch',
__( 'Increase your ad revenue', 'advanced-ads' ),
[ $this, 'display_metabox' ],
Constants::POST_TYPE_AD,
'side',
'low'
);
}
if ( ! defined( 'AAT_VERSION' ) ) {
add_meta_box(
'advads-tracking-pitch',
__( 'Statistics', 'advanced-ads' ),
[ $this, 'display_metabox' ],
Constants::POST_TYPE_AD,
'normal',
'low'
);
}
}
/**
* Display metaboxes by their id.
*
* @param WP_Post $post WP_Post object.
* @param array $box meta box information.
*
* @return void
*/
public function display_metabox( $post, $box ): void {
$views = [
'advads-pro-pitch' => 'marketing/ad-metabox-all-access.php',
'advads-tracking-pitch' => 'marketing/ad-metabox-tracking.php',
];
$view = $views[ $box['id'] ] ?? false;
if ( $view ) {
require_once ADVADS_ABSPATH . 'views/' . $view;
}
}
}

View File

@@ -0,0 +1,133 @@
<?php
/**
* Ad settings metabox.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use Advanced_Ads;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Validation;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Ad settings metabox.
*/
class Metabox_Ad_Settings implements Integration_Interface {
/**
* Ad setting post meta key
*
* @var string
*/
const SETTING_METAKEY = '_advads_ad_settings';
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ] );
add_action( 'save_post', [ $this, 'save_settings' ], 10, 2 );
}
/**
* Add a meta box to post type edit screens with ad settings
*
* @param string $post_type current post type.
*
* @return void
*/
public function add_meta_box( $post_type = '' ): void {
// Early bail!!
if ( ! Conditional::user_can( 'advanced_ads_edit_ads' ) || ! is_post_type_viewable( $post_type ) ) {
return;
}
$options = Advanced_Ads::get_instance()->options();
$disabled_post_types = $options['pro']['general']['disable-by-post-types'] ?? [];
$render_what = in_array( $post_type, $disabled_post_types, true ) ? 'display_disable_notice' : 'display_settings';
add_meta_box(
'advads-ad-settings',
__( 'Ad Settings', 'advanced-ads' ),
[ $this, $render_what ],
$post_type,
'side',
'low'
);
}
/**
* Render meta box for ad settings notice when ads disabled for post type
*
* @param WP_Post $post The post object.
*
* @return void
*/
public function display_disable_notice( $post ): void {
$labels = get_post_type_object( $post->post_type )->labels;
include ADVADS_ABSPATH . 'views/notices/ad-disable-post-type.php';
}
/**
* Render meta box for ad settings on a per post basis
*
* @param WP_Post $post The post object.
*
* @return void
*/
public function display_settings( $post ): void {
$values = get_post_meta( $post->ID, self::SETTING_METAKEY, true );
include ADVADS_ABSPATH . 'views/admin/metaboxes/ads/post-ad-settings.php';
}
/**
* Save the ad settings when the post is saved.
*
* @param int $post_id Post ID.
* @param object $post Post object.
*
* @return void
*/
public function save_settings( $post_id, $post ): void {
$post_id = absint( $post_id );
// Check the nonce.
$nonce = Params::post( 'advads_post_meta_box_nonce' );
if ( ! $nonce || ! wp_verify_nonce( $nonce, 'advads_post_meta_box' ) ) {
return;
}
// Dont display for non admins.
if (
! Conditional::user_can( 'advanced_ads_edit_ads' ) ||
! Validation::check_save_post( $post_id, $post )
) {
return;
}
// Check user has permission to edit.
$perm = 'page' === get_post_type( $post_id ) ? 'edit_page' : 'edit_post';
if ( ! current_user_can( $perm, $post_id ) ) {
return;
}
$data['disable_ads'] = absint( $_POST['advanced_ads']['disable_ads'] ?? 0 );
$data = apply_filters( 'advanced_ads_save_post_meta_box', $data );
update_post_meta( $post_id, self::SETTING_METAKEY, $data );
}
}

View File

@@ -0,0 +1,454 @@
<?php
/**
* The class is responsible for adding metaboxes to the ad edit screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use Advanced_Ads_AdSense_Data;
use Advanced_Ads_AdSense_Admin;
use Advanced_Ads_Ad_Type_Adsense;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Validation;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Ad Metabox.
*/
class Metabox_Ad implements Integration_Interface {
/**
* Ad being shown on the screen.
*
* @var Ad|null
*/
protected $ad = null;
/**
* Our boxes ids.
*
* @var array
*/
protected $meta_box_ids = [
'advads-pro-pitch',
'advads-tracking-pitch',
'revisionsdiv',
'advanced_ads_groupsdiv',
];
/**
* Hold metaboxes objects
*
* @var array
*/
protected $metaboxes = [];
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
$this->register_metaboxes();
add_action( 'add_meta_boxes_' . Constants::POST_TYPE_AD, [ $this, 'add_meta_boxes' ] );
add_filter( 'hidden_meta_boxes', [ $this, 'unhide_meta_boxes' ], 10, 2 );
add_filter( 'postbox_classes_advanced_ads_ad-types-box', [ $this, 'add_classes_ad_type' ] );
add_filter( 'pre_wp_unique_post_slug', [ $this, 'pre_wp_unique_post_slug' ], 10, 5 );
add_filter( 'wp_insert_post_data', [ $this, 'set_post_title' ] );
add_action( 'save_post_advanced_ads', [ $this, 'save' ], 10, 2 );
}
/**
* Pre-fetch any data for the screen.
*
* @param int $post_id Post ID being shown.
*
* @return void
*/
protected function prepare_ad( $post_id ): void {
if ( empty( $this->ad ) || $this->ad->get_id() !== $post_id ) {
$this->ad = wp_advads_get_ad( $post_id );
}
}
/**
* Add "close" class to collapse the ad-type metabox after ad was saved first
*
* @param array $classes Metabox classes.
*
* @return array
*/
public function add_classes_ad_type( $classes = [] ): array {
global $post;
if (
isset( $post->ID ) &&
'publish' === $post->post_status &&
! in_array( 'closed', $classes, true )
) {
$classes[] = 'closed';
}
return $classes;
}
/**
* Force all AA related meta boxes to stay visible
*
* @param array $hidden An array of hidden meta boxes.
* @param WP_Screen $screen Current screen instance.
*
* @return array
*/
public function unhide_meta_boxes( $hidden, $screen ): array {
if ( ! isset( $screen->id ) || 'advanced_ads' !== $screen->id ) {
return $hidden;
}
return array_diff( $hidden, (array) apply_filters( 'advanced-ads-unhide-meta-boxes', $this->meta_box_ids ) );
}
/**
* Add meta boxes
*
* @return void
*/
public function add_meta_boxes(): void {
foreach ( $this->metaboxes as $metabox ) {
$metabox->register( $this );
}
add_filter( 'wp_dropdown_cats', [ $this, 'remove_parent_group_dropdown' ], 10, 2 );
}
/**
* Display metaboxes by their id.
*
* @param WP_Post $post Post instance.
* @param array $box Meta box information.
*
* @return void
*/
public function display( $post, $box ): void {
$this->prepare_ad( $post->ID );
$ad = $this->ad;
$metabox = $this->metaboxes[ $box['id'] ] ?? false;
if ( $metabox ) {
$this->display_warnings( $box );
$view = $metabox->get_view( $ad );
if ( $view ) {
include $view;
}
$this->display_handle_links( $metabox );
}
}
/**
* Remove parent group dropdown from ad group taxonomy
*
* @param string $output Parent group dropdown HTML.
* @param array $arguments Additional parameters.
*
* @return string
*/
public function remove_parent_group_dropdown( $output, $arguments ): string {
if ( 'newadvanced_ads_groups_parent' === $arguments['name'] ) {
$output = '';
}
return $output;
}
/**
* Create a unique across all post types slug for the ad.
* Almost all code here copied from `wp_unique_post_slug()`.
*
* @param string $override_slug Short-circuit return value.
* @param string $slug The desired slug (post_name).
* @param int $post_id Post ID.
* @param string $post_status The post status.
* @param string $post_type Post type.
*
* @return string|null
*/
public function pre_wp_unique_post_slug( $override_slug, $slug, $post_id, $post_status, $post_type ) {
global $wpdb, $wp_rewrite;
// Early bail!!
if ( Constants::POST_TYPE_AD !== $post_type ) {
return $override_slug;
}
$feeds = $wp_rewrite->feeds;
if ( ! is_array( $feeds ) ) {
$feeds = [];
}
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_id ) );
if ( $post_name_check || in_array( $slug, $feeds, true ) || 'embed' === $slug ) {
$suffix = 2;
do {
$alt_post_name = substr( $slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_id ) );
++$suffix;
} while ( $post_name_check );
$override_slug = $alt_post_name;
}
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
// phpcs:enable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery
return $override_slug;
}
/**
* Prepare main post data for ads when being saved.
*
* @param array $data An array of slashed post data.
*
* @return array
*/
public function set_post_title( $data ): array {
if (
Constants::POST_TYPE_AD === $data['post_type'] &&
'' === $data['post_title']
) {
$created_time = function_exists( 'wp_date' )
? wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) )
: $data['post_date'];
/* translators: %s is the time the ad was first saved. */
$data['post_title'] = sprintf( __( 'Ad created on %s', 'advanced-ads' ), $created_time );
}
return $data;
}
/**
* Save the ad post type to be saved.
*
* @param int $post_id Post ID.
* @param object $post Post object.
*
* @return void
*/
public function save( $post_id, $post ): void {
$post_id = absint( $post_id );
if (
! Conditional::user_can( 'advanced_ads_edit_ads' ) ||
! Validation::check_save_post( $post_id, $post )
) {
return;
}
// If new post.
$post_data = self::get_post_data();
if ( empty( $post_data ) ) {
return;
}
// Maybe there is a type change so force it.
$ad = wp_advads_get_ad( $post_id, wp_unslash( $post_data['type'] ) );
$ad->set_props( $post_data );
remove_action( 'save_post_advanced_ads', [ $this, 'save' ], 10, 2 );
$ad->save();
add_action( 'save_post_advanced_ads', [ $this, 'save' ], 10, 2 );
}
/**
* Get posted data for ad.
*
* @return array
*/
public static function get_post_data(): array {
$data = Params::post( 'advanced_ad', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$data = array_merge( $data ?? [], $data['output'] ?? [] );
unset( $data['output'] );
// Filters to manipulate options or add more to be saved.
$data = apply_filters_deprecated(
'advanced-ads-save-options',
[ $data, null ],
'1.48.2',
'advanced-ads-ad-pre-save',
__( 'Use advanced-ads-ad-pre-save action', 'advanced-ads' )
);
$data = apply_filters_deprecated(
'advanced-ads-ad-settings-pre-save',
[ $data ],
'1.48.2',
'advanced-ads-ad-pre-save',
__( 'Use advanced-ads-ad-pre-save action', 'advanced-ads' )
);
return $data;
}
/**
* Render links for handles
*
* @param object $metabox Metabox instance.
*
* @return void
*/
private function display_handle_links( $metabox ): void {
// Early bail!!
if ( ! $metabox->get_handle_link() ) {
return;
}
?>
<span class="advads-hndlelinks hidden">
<?php
$link = $metabox->get_handle_link();
if ( is_array( $link ) ) {
$link = join( '', $link );
}
echo wp_kses(
$link,
[
'a' => [
'target' => [],
'href' => [],
'class' => [],
],
]
);
?>
</span>
<?php
if ( 'ad-targeting-box' === $metabox->get_box_id() ) :
?>
<div class="advads-video-link-container" data-videolink='<iframe width="420" height="315" src="https://www.youtube-nocookie.com/embed/VjfrRl5Qn4I?rel=0&amp;showinfo=0" frameborder="0" allowfullscreen></iframe>'></div>
<?php
endif;
}
/**
* Render links for handles
*
* @param array $box Meta box information.
*
* @return void
*/
private function display_warnings( $box ): void {
$warnings = [];
$box_id = $box['id'];
/**
* List general notices
* elements in $warnings contain [text] and [class] attributes.
*/
if ( 'ad-parameters-box' === $box_id ) {
$warnings[] = [
'text' => Advanced_Ads_AdSense_Admin::get_auto_ads_messages()[ Advanced_Ads_AdSense_Data::get_instance()->is_page_level_enabled() ? 'enabled' : 'disabled' ],
'class' => 'advads-auto-ad-in-ad-content hidden advads-notice-inline advads-error',
];
// Show warning if ad contains https in parameters box.
$https_message = Validation::is_ad_https( $this->ad );
if ( $https_message ) {
$warnings[] = [
'text' => $https_message,
'class' => 'advads-ad-notice-https-missing advads-notice-inline advads-error',
];
}
}
// Let users know that they could use the Google AdSense ad type
// when they enter an AdSense code.
if ( 'ad-parameters-box' === $box_id && $this->has_adsense_on_plain_content_type() ) {
$adsense_auto_ads = Advanced_Ads_AdSense_Data::get_instance()->is_page_level_enabled();
$warnings[] = [
'class' => 'advads-adsense-found-in-content advads-notice-inline advads-error',
'text' => sprintf(
/* translators: %1$s opening button tag, %2$s closing button tag. */
esc_html__( 'This looks like an AdSense ad. Switch the ad type to “AdSense ad” to make use of more features. %1$sSwitch to AdSense ad%2$s.', 'advanced-ads' ),
'<button class="button-secondary" id="switch-to-adsense-type">',
'</button>'
),
];
}
$warnings = apply_filters( 'advanced-ads-ad-notices', $warnings, $box, $this->ad );
echo '<ul id="' . esc_attr( $box_id ) . '-notices" class="advads-metabox-notices">';
foreach ( $warnings as $warning ) {
if ( isset( $warning['text'] ) ) {
printf(
'<li class="%s">%s</li>',
esc_attr( $warning['class'] ?? '' ),
$warning['text'], // phpcs:ignore
);
}
}
echo '</ul>';
}
/**
* Is using Google Adsense on plain and content ad type
*
* @return boolean
*/
private function has_adsense_on_plain_content_type(): bool {
$content = $this->ad->get_content();
if (
Advanced_Ads_Ad_Type_Adsense::content_is_adsense( $content ) &&
$this->ad->is_type( [ 'plain', 'content' ] ) &&
false === strpos( $content, 'enable_page_level_ads' ) &&
! preg_match( '/script[^>]+data-ad-client=/', $content )
) {
return true;
}
return false;
}
/**
* Register metaboxes
*
* @return void
*/
private function register_metaboxes(): void {
$this->register_metabox( Metaboxes\Ad_Usage::class );
$this->register_metabox( Metaboxes\Ad_Types::class );
$this->register_metabox( Metaboxes\Ad_Parameters::class );
$this->register_metabox( Metaboxes\Ad_Layout::class );
$this->register_metabox( Metaboxes\Ad_Targeting::class );
$this->register_metabox( Metaboxes\Ad_Adsense::class );
}
/**
* Register metabox
*
* @param string $metabox_class Metabox class name.
*
* @return void
*/
private function register_metabox( $metabox_class ): void {
$metabox = new $metabox_class();
$metabox_id = $metabox->get_box_id();
$this->meta_box_ids[] = $metabox_id;
$this->metaboxes[ $metabox_id ] = $metabox;
}
}

View File

@@ -0,0 +1,279 @@
<?php
/**
* Uncategorised functionality.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.x.x
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Constants;
use Advanced_Ads_Admin_Notices;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Importers\XML_Importer;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Class Misc
*/
class Misc implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'gettext', [ $this, 'replace_cheating_message' ], 20, 2 );
add_filter( 'get_user_option_user-settings', [ $this, 'reset_view_mode_option' ] );
add_action( 'in_admin_header', [ $this, 'register_admin_notices' ] );
add_action( 'plugins_api_result', [ $this, 'recommend_suitable_add_ons' ], 11, 3 );
add_action( 'admin_action_advanced_ads_starter_setup', [ $this, 'import_starter_setup' ] );
}
/**
* Replace 'You need a higher level of permission.' message if user role does not have required permissions.
*
* @param string $translated_text Translated text.
* @param string $untranslated_text Text to translate.
*
* @return string $translation Translated text.
*/
public function replace_cheating_message( $translated_text, $untranslated_text ): string {
global $typenow;
if (
isset( $typenow )
&& 'You need a higher level of permission.' === $untranslated_text
&& Constants::POST_TYPE_AD === $typenow
) {
$translated_text = __( 'You dont have access to ads. Please deactivate and re-enable Advanced Ads again to fix this.', 'advanced-ads' )
. '&nbsp;<a href="https://wpadvancedads.com/manual/user-capabilities/?utm_source=advanced-ads&utm_medium=link&utm_campaign=wrong-user-role#You_dont_have_access_to_ads" target="_blank">' . __( 'Get help', 'advanced-ads' ) . '</a>';
}
return (string) $translated_text;
}
/**
* Set the removed post list mode to "List", if it was set to "Excerpt".
*
* @param string $user_options Query string containing user options.
*
* @return string
*/
public function reset_view_mode_option( $user_options ): string {
return str_replace( '&posts_list_mode=excerpt', '&posts_list_mode=list', $user_options );
}
/**
* Registers Advanced Ads admin notices
* prevents other notices from showing up on our own pages
*
* @return void
*/
public function register_admin_notices(): void {
/**
* Remove all registered admin_notices from AA screens
* we need to use this or some users have half or more of their viewports cluttered with unrelated notices
*/
if ( Conditional::is_screen_advanced_ads() ) {
remove_all_actions( 'admin_notices' );
}
add_action( 'admin_notices', [ $this, 'admin_notices' ] );
}
/**
* Initiate the admin notices class
*
* @return void
*/
public function admin_notices(): void {
// Display ad block warning to everyone who can edit ads.
if (
Conditional::user_can( 'advanced_ads_edit_ads' )
&& Conditional::is_screen_advanced_ads()
) {
$ad_blocker_notice_id = wp_advads()->get_frontend_prefix() . 'abcheck-' . md5( microtime() );
wp_register_script( $ad_blocker_notice_id . '-adblocker-notice', false, [], ADVADS_VERSION, true );
wp_enqueue_script( $ad_blocker_notice_id . '-adblocker-notice' );
wp_add_inline_script(
$ad_blocker_notice_id . '-adblocker-notice',
"jQuery( document ).ready( function () {
if ( typeof advanced_ads_adblocker_test === 'undefined' ) {
jQuery( '#" . esc_attr( $ad_blocker_notice_id ) . ".message' ).show();
}
} );"
);
include_once ADVADS_ABSPATH . 'admin/views/notices/adblock.php';
}
// Show success notice after starter setup was imported. Registered here because it will be visible only once.
if ( 'advanced-ads-starter-setup-success' === Params::get( 'message' ) ) {
add_action( 'advanced-ads-admin-notices', [ $this, 'starter_setup_success_message' ] );
}
/*
Register our own notices on Advanced Ads pages, except
-> the overview page where they should appear in the notices section,
-> revision page to prevent duplicate revision controls.
*/
$screen = get_current_screen();
if (
Conditional::user_can( 'advanced_ads_edit_ads' )
&& ( ! isset( $screen->id ) || 'toplevel_page_advanced-ads' !== $screen->id )
&& 'revision' !== $screen->id
) {
echo '<div class="wrap">';
Advanced_Ads_Admin_Notices::get_instance()->display_notices();
// Allow other Advanced Ads plugins to show admin notices at this late stage.
do_action( 'advanced-ads-admin-notices' );
echo '</div>';
}
}
/**
* Show success message after starter setup was created.
*
* @return void
*/
public function starter_setup_success_message(): void {
$last_post = get_posts( [ 'numberposts' => 1 ] );
$last_post_link = isset( $last_post[0]->ID ) ? get_permalink( $last_post[0]->ID ) : false;
include ADVADS_ABSPATH . 'admin/views/notices/starter-setup-success.php';
}
/**
* Recommend additional add-ons
*
* @param object|WP_Error $result Response object or WP_Error.
* @param string $action The type of information being requested from the Plugin Installation API.
* @param object $args Plugin API arguments.
*
* @return object|WP_Error Response object or WP_Error.
*/
public function recommend_suitable_add_ons( $result, $action, $args ) {
if (
empty( $args->browse )
|| ! in_array( $args->browse, [ 'featured', 'recommended', 'popular' ], true )
|| ( isset( $result->info['page'] ) && $result->info['page'] > 1 )
) {
return $result;
}
// Grab all slugs from the api results.
$result_slugs = wp_list_pluck( $result->plugins, 'slug' );
// Recommend AdSense In-Feed add-on.
$result = $this->recommend_plugin(
'advanced-ads-adsense-in-feed',
'advanced-ads-adsense-in-feed/advanced-ads-in-feed.php',
$args,
$result,
$result_slugs
);
// Recommend Genesis Ads add-on.
if ( defined( 'PARENT_THEME_NAME' ) && 'Genesis' === PARENT_THEME_NAME ) {
$result = $this->recommend_plugin(
'advanced-ads-genesis',
'advanced-ads-genesis/genesis-ads.php',
$args,
$result,
$result_slugs
);
}
// Recommend WP Bakery (former Visual Composer) add-on.
if ( defined( 'WPB_VC_VERSION' ) ) {
$result = $this->recommend_plugin(
'ads-for-visual-composer',
'ads-for-visual-composer/advanced-ads-vc.php',
$args,
$result,
$result_slugs
);
}
return $result;
}
/**
* Import a starter setup for new users
*
* @return void
*/
public function import_starter_setup(): void {
if (
'advanced_ads_starter_setup' !== Params::get( 'action' )
|| ! Conditional::user_can( 'advanced_ads_edit_ads' )
) {
return;
}
check_admin_referer( 'advanced-ads-starter-setup' );
$xml = file_get_contents( ADVADS_ABSPATH . 'admin/assets/xml/starter-setup.xml' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
( new XML_Importer() )->import_content( $xml );
// Redirect to the ads overview page.
wp_safe_redirect( admin_url( 'edit.php?post_type=advanced_ads&message=advanced-ads-starter-setup-success' ) );
}
/**
* Recommends a plugin based on the provided slug and file path.
*
* This function checks if the plugin is already active or recommended. If not,
* it fetches the plugin data using the WordPress Plugin API and adds it to the result.
*
* @param string $slug The slug of the plugin to recommend.
* @param string $file_path The file path of the plugin.
* @param object $args Additional arguments for the recommendation.
* @param object $result The current result object containing recommended plugins.
* @param array $result_slugs An array of slugs of already recommended plugins.
*
* @return object The updated result object with the recommended plugin added.
*/
private function recommend_plugin( $slug, $file_path, $args, $result, $result_slugs ) {
// Check if the plugin is already active or recommended.
if (
is_plugin_active( $file_path )
|| is_plugin_active_for_network( $file_path )
|| in_array( $slug, $result_slugs, true )
) {
return $result;
}
// Prepare query arguments to fetch plugin data.
$query_args = [
'slug' => $slug,
'fields' => [
'icons' => true,
'active_installs' => true,
'short_description' => true,
'group' => true,
],
];
$plugin_data = plugins_api( 'plugin_information', $query_args );
// Add plugin data to the result if fetched successfully.
if ( ! is_wp_error( $plugin_data ) ) {
if ( 'featured' === $args->browse ) {
array_push( $result->plugins, $plugin_data );
} else {
array_unshift( $result->plugins, $plugin_data );
}
}
return $result;
}
}

View File

@@ -0,0 +1,134 @@
<?php
/**
* Admin Page Quick Edit.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Utilities\Formatting;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Page Quick Edit.
*/
class Page_Quick_Edit implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'quick_edit_custom_box', [ $this, 'add_quick_edit_fields' ], 10, 2 );
add_action( 'bulk_edit_custom_box', [ $this, 'add_bulk_edit_fields' ], 10, 2 );
add_action( 'save_post', [ $this, 'save_quick_edit_fields' ] );
add_action( 'save_post', [ $this, 'save_bulk_edit_fields' ] );
}
/**
* Save bulk changes
*
* @return void
*/
public function save_bulk_edit_fields() {
// Not bulk edit, not post/page or not enough permissions.
if (
! wp_verify_nonce( sanitize_key( Params::get( '_wpnonce', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ), 'bulk-posts' )
|| ! in_array( sanitize_key( Params::get( 'post_type' ) ), [ 'post', 'page' ], true )
|| ! current_user_can( 'edit_posts' )
) {
return;
}
$disable_ads = Params::get( 'advads_disable_ads', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$disable_the_content = Params::get( 'advads_disable_the_content', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( empty( $disable_ads ) && empty( $disable_the_content ) ) {
return;
}
$ids = Params::get( 'post', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
foreach ( $ids as $id ) {
$meta = get_post_meta( (int) $id, '_advads_ad_settings', true );
if ( ! empty( $disable_ads ) ) {
$meta['disable_ads'] = Formatting::string_to_bool( $disable_ads ) ? 1 : 0;
}
if ( ! empty( $disable_the_content ) ) {
$meta['disable_the_content'] = Formatting::string_to_bool( $disable_the_content ) ? 1 : 0;
}
update_post_meta( (int) $id, '_advads_ad_settings', $meta );
}
}
/**
* Print bulk edit fields
*
* @param string $column_name the column name.
* @param string $post_type current post type.
*
* @return void
*/
public function add_bulk_edit_fields( $column_name, $post_type ) {
if ( ! in_array( $post_type, [ 'post', 'page' ], true ) ) {
return;
}
require ADVADS_ABSPATH . 'views/admin/page-bulk-edit.php';
}
/**
* Save quick edit changes
*
* @param int $post_id the post id.
*
* @return void
*/
public function save_quick_edit_fields( $post_id ) {
// Not inline edit, or no permission.
if (
! wp_verify_nonce( sanitize_key( Params::post( '_inline_edit' ) ), 'inlineeditnonce' ) ||
! current_user_can( 'edit_post', $post_id )
) {
return;
}
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( wp_is_post_revision( $post_id ) ) {
return;
}
$meta = [ 'disable_ads' => Params::post( 'advads-disable-ads', 0, FILTER_VALIDATE_INT ) ];
if ( defined( 'AAP_VERSION' ) ) {
$meta['disable_the_content'] = Params::post( 'advads-disable-the-content', 0 );
}
update_post_meta( $post_id, '_advads_ad_settings', $meta );
}
/**
* Print quick edit fields.
*
* @param string $column the column name.
* @param string $post_type current post type.
*
* @return void
*/
public function add_quick_edit_fields( $column, $post_type ) {
if ( ! in_array( $post_type, [ 'post', 'page' ], true ) ) {
return;
}
if ( 'ad-status' === $column ) {
require ADVADS_ABSPATH . 'views/admin/page-quick-edit.php';
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* Placement Create Modal.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Modal;
use AdvancedAds\Entities;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placement Create Modal.
*/
class Placement_Create_Modal implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_footer', [ $this, 'render_modal' ] );
}
/**
* Load the modal for creating a new Placement.
*
* @return void
*/
public function render_modal(): void {
global $wp_query;
ob_start();
$placements_description = 0 === $wp_query->found_posts ? Entities::get_placement_description() : '';
include ADVADS_ABSPATH . 'views/admin/placements/create-modal/new-modal-content.php';
// @TODO Add old JS validation errors to the Modal - advads_validate_new_form in `admin/assets/js/admin.js`.
Modal::create(
[
'modal_slug' => 'placement-new',
'modal_content' => ob_get_clean(),
'modal_title' => __( 'New Placement', 'advanced-ads' ),
'close_action' => __( 'Save New Placement', 'advanced-ads' ),
'close_form' => 'advads-placements-new-form',
'close_validation' => 'advads_validate_new_form',
]
);
}
}

View File

@@ -0,0 +1,396 @@
<?php
/**
* Placement Edit Modal.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Modal;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Utilities\Content_Injection;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placement Edit Modal.
*/
class Placement_Edit_Modal implements Integration_Interface {
/**
* Hold placement
*
* @var Placement
*/
private $placement = null;
/**
* View path
*
* @var string
*/
private $view_path = null;
/**
* The constructor
*
* @param Placement $placement Placement instance.
*/
public function __construct( $placement ) {
$this->placement = $placement;
$this->view_path = ADVADS_ABSPATH . 'views/admin/placements/edit-modal';
}
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_footer', [ $this, 'render_modal' ] );
}
/**
* Load the modal for creating a new Placement.
*
* @return void
*/
public function render_modal(): void {
Modal::create(
[
'modal_slug' => 'placement-edit-' . $this->placement->get_id(),
'modal_content' => $this->render_modal_content(),
/* translators: 1: "Options", 2: the name of a placement. */
'modal_title' => sprintf( '%1$s: %2$s', __( 'Options', 'advanced-ads' ), $this->placement->get_title() ),
]
);
}
/**
* Get form id
*
* @return string
*/
private function get_form_id(): string {
return 'advanced-ads-placement-form-' . $this->placement->get_id();
}
/**
* Render the placement modal content as if it was the single edit page for a post.
*
* @return string
*/
private function render_modal_content(): string {
ob_start();
$placement = $this->placement;
$user_id = wp_get_current_user()->ID;
$author_id = get_post_field( 'post_author', $this->placement->get_id() );
include $this->view_path . '/edit-modal-content.php';
return ob_get_clean();
}
/**
* Render settings.
*
* @return void
*/
private function render_settings(): void {
$basic_settings = [
'placement-name' => [
'label' => __( 'Name', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_name' ],
'description' => '',
'order' => 5,
],
'placement-item' => [
'label' => __( 'Item', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_item' ],
'description' => '',
'order' => 5,
],
'placement-status' => [
'label' => __( 'Status', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_status' ],
'description' => '',
'order' => 5,
],
];
if ( $this->placement->is_type( 'post_content' ) ) {
$basic_settings['placement-content-injection-index'] = [
'label' => __( 'position', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_position' ],
'description' => '',
'order' => 6,
];
}
self::render_settings_group( $basic_settings );
$slug = $this->placement->get_slug();
/**
* Hook before advanced placement options
*
* @param string $slug the placement slug.
* @param Placement $placement the placement.
*/
do_action( 'advanced-ads-placement-options-before-advanced', $slug, $this->placement );
$type_options = $this->placement->get_type_object()->get_options();
$advanced_settings = [];
if ( ! $this->placement->is_type( 'header' ) ) {
if ( $type_options['placement-ad-label'] ?? true ) {
$advanced_settings['placement-ad-label'] = [
'label' => __( 'ad label', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_ad_label' ],
'description' => '',
'order' => 7,
];
}
if ( isset( $type_options['show_position'] ) && $type_options['show_position'] ) {
$advanced_settings['placement-position'] = [
'label' => __( 'Position', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_label_position' ],
'description' => '',
'order' => 7,
];
}
$advanced_settings['placement-inline-css'] = [
'label' => __( 'Inline CSS', 'advanced-ads' ),
'callback' => [ $this, 'render_placement_inline_css' ],
'description' => '',
'order' => 9,
];
if ( ! defined( 'AAP_VERSION' ) ) {
// Minimum Content Length.
$advanced_settings['placement-content-minimum-length'] = [
'label' => __( 'Minimum Content Length', 'advanced-ads' ),
'callback' => [ $this, 'render_pro_pitch' ],
'description' => __( 'Minimum length of content before automatically injected ads are allowed in them.', 'advanced-ads' ),
'order' => 10,
];
// Words Between Ads.
$advanced_settings['placement-skip-paragraph'] = [
'label' => __( 'Words Between Ads', 'advanced-ads' ),
'callback' => [ $this, 'render_pro_pitch' ],
'description' => __( 'A minimum amount of words between automatically injected ads.', 'advanced-ads' ),
'order' => 10,
];
}
}
// show the conditions pitch on the `head` placement as well.
if ( ! defined( 'AAP_VERSION' ) ) {
// Display Conditions for placements.
$advanced_settings['placement-display-conditions'] = [
'label' => __( 'Display Conditions', 'advanced-ads' ),
'callback' => [ $this, 'render_pro_pitch' ],
'description' => __( 'Use display conditions for placements.', 'advanced-ads' ) . ' ' . __( 'The free version provides conditions on the ad edit page.', 'advanced-ads' ),
'order' => 10,
];
// Visitor Condition for placements.
$advanced_settings['placement-visitor-conditions'] = [
'label' => __( 'Visitor Conditions', 'advanced-ads' ),
'callback' => [ $this, 'render_pro_pitch' ],
'description' => __( 'Use visitor conditions for placements.', 'advanced-ads' ) . ' ' . __( 'The free version provides conditions on the ad edit page.', 'advanced-ads' ),
'order' => 10,
];
}
if ( $advanced_settings ) {
self::render_settings_group( $advanced_settings );
}
/**
* Hook after advanced placement options
*
* @param string $slug the placement slug.
* @param Placement $placement the placement.
*/
do_action( 'advanced-ads-placement-options-after-advanced', $slug, $this->placement );
}
/**
* Render a group of placements options
*
* @param array $settings placement option fields.
*
* @return void
*/
private static function render_settings_group( $settings ) {
array_multisort( array_column( $settings, 'order' ), SORT_ASC, $settings );
foreach ( $settings as $setting_id => $setting ) {
WordPress::render_option(
$setting_id,
$setting['label'],
$setting['callback']( $setting_id ),
$setting['description']
);
}
}
/**
* Render the placement name option.
*
* @return string
*/
private function render_placement_name(): string {
$placement = $this->placement;
ob_start();
include $this->view_path . '/fields/name.php';
return ob_get_clean();
}
/**
* Render the placement item option.
*
* @return string
*/
private function render_placement_item(): string {
$placement = $this->placement;
ob_start();
include $this->view_path . '/fields/item.php';
return ob_get_clean();
}
/**
* Render the placement status option.
*
* @return string
*/
private function render_placement_status(): string {
$placement = $this->placement;
ob_start();
include $this->view_path . '/fields/status.php';
return ob_get_clean();
}
/**
* Render placement ad label option.
*
* @return string
*/
private function render_placement_ad_label(): string {
$label = $this->placement->get_prop( 'ad_label' ) ?? 'default';
ob_start();
include $this->view_path . '/fields/ad-label.php';
return ob_get_clean();
}
/**
* Render the placement label position option.
*
* @return string
*/
private function render_placement_label_position(): string {
$position = $this->placement->get_prop( 'placement_position' ) ?? 'default';
$clearfix = ! empty( $this->placement->get_prop( 'placement_clearfix' ) );
ob_start();
include $this->view_path . '/fields/ad-label-position.php';
return ob_get_clean();
}
/**
* Render the placement inline CSS option.
*
* @return string
*/
private function render_placement_inline_css(): string {
$placement = $this->placement;
$placement_slug = $this->placement->get_slug();
$inline_css = $this->placement->get_prop( 'inline-css' ) ?? '';
ob_start();
include $this->view_path . '/fields/inline-css.php';
return ob_get_clean();
}
/**
* Return `is_pro_pitch` for the options that are pitching the pro version.
* WordPress::render_option() handles this string as a special case.
*
* @return string
*/
private function render_pro_pitch(): string {
return 'is_pro_pitch';
}
/**
* Render the placement position
*
* @return string
*/
public function render_placement_position() {
$data = $this->placement->get_data();
$placement_slug = $this->placement->get_slug();
$index = max( 1, (int) ( $data['index'] ?? 1 ) );
$tags = Content_Injection::get_tags();
$selected_tag = $data['tag'] ?? 'p';
// Automatically select the 'custom' option.
if ( Params::cookie( 'advads_frontend_picker', '' ) === $placement_slug ) {
$selected_tag = 'custom';
}
$xpath = stripslashes( $data['xpath'] ?? '' );
$positions = [
'after' => __( 'after', 'advanced-ads' ),
'before' => __( 'before', 'advanced-ads' ),
];
$selected_position = $data['position'] ?? 'after';
$start_from_bottom = isset( $data['start_from_bottom'] );
ob_start();
include $this->view_path . '/fields/content-index.php';
if ( ! defined( 'AAP_VERSION' ) ) {
include ADVADS_ABSPATH . 'admin/views/upgrades/repeat-the-position.php';
}
do_action( 'advanced-ads-placement-post-content-position', $placement_slug, $this->placement );
if ( ! extension_loaded( 'dom' ) ) :
?>
<p>
<span class="advads-notice-inline advads-error">
<?php esc_html_e( 'Important Notice', 'advanced-ads' ); ?>:
</span>
<?php
printf(
/* translators: %s is a name of a module. */
esc_html__( 'Missing PHP extension could cause issues. Please ask your hosting provider to enable it: %s', 'advanced-ads' ),
'dom (php_xml)'
);
?>
</p>
<?php
endif;
return ob_get_clean();
}
}

View File

@@ -0,0 +1,368 @@
<?php
/**
* Placement List Table.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.49.0
*/
namespace AdvancedAds\Admin;
use WP_Query;
use AdvancedAds\Modal;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Admin_List_Table;
use AdvancedAds\Framework\Utilities\Params;
defined( 'ABSPATH' ) || exit;
/**
* Placement List Table.
*/
class Placement_List_Table extends Admin_List_Table {
/**
* Object being shown on the row.
*
* @var Placement|null
*/
protected $object = null;
/**
* Object type.
*
* @var string
*/
protected $object_type = 'placement';
/**
* Post type.
*
* @var string
*/
protected $list_table_type = Constants::POST_TYPE_PLACEMENT;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
parent::hooks();
add_action( 'manage_posts_extra_tablenav', [ $this, 'placements_list_after' ] );
add_action( 'manage_posts_extra_tablenav', [ $this, 'display_views' ] );
// Manage rows and columns.
add_filter( 'list_table_primary_column', [ $this, 'set_primary_column' ], 10, 0 );
add_filter( 'post_row_actions', [ $this, 'row_actions' ] );
// Filters.
add_filter( 'disable_months_dropdown', '__return_true' );
}
/**
* Define hidden columns.
*
* @return array
*/
protected function define_hidden_columns(): array {
return [ 'id', 'title' ];
}
/**
* Define which columns to show on this screen.
*
* @param array $columns Existing columns.
*
* @return array
*/
public function define_columns( $columns ): array {
return [
'cb' => $columns['cb'],
'type' => __( 'Type', 'advanced-ads' ),
'title' => __( 'Title', 'advanced-ads' ),
'name' => __( 'Name', 'advanced-ads' ),
'ad_group' => sprintf( '%1$s / %2$s', __( 'Ad', 'advanced-ads' ), __( 'Group', 'advanced-ads' ) ),
'conditions' => __( 'Delivery', 'advanced-ads' ),
];
}
/**
* Define which columns are sortable.
*
* @param array $columns Existing columns.
*
* @return array
*/
public function define_sortable_columns( $columns ): array {
$columns['type'] = 'type';
$columns['name'] = 'title';
return $columns;
}
/**
* Pre-fetch any data for the row each column has access to it.
*
* @param int $post_id Post ID being shown.
*
* @return void
*/
protected function prepare_row_data( $post_id ): void {
if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) {
$this->object = wp_advads_get_placement( $post_id );
}
}
/**
* Set the primary column.
*
* @return string
*/
public function set_primary_column(): string {
return 'name';
}
/**
* Displays the list of views available for Placements.
*
* @param string $which The location of the extra table nav markup.
*
* @return void
*/
public function display_views( $which ): void {
global $wp_list_table;
if ( 'top' !== $which ) {
return;
}
$views = $wp_list_table->get_views();
/**
* Filters the list of available list table views.
*
* The dynamic portion of the hook name, `$this->screen->id`, refers
* to the ID of the current screen.
*
* @param string[] $views An array of available list table views.
*/
$views = apply_filters( "views_{$wp_list_table->screen->id}", $views );
if ( empty( $views ) ) {
return;
}
$wp_list_table->screen->render_screen_reader_content( 'heading_views' );
$is_all = count(
array_diff_key(
$_GET, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
[
'post_type' => Constants::POST_TYPE_AD,
'orderby' => '',
'order' => '',
'paged' => '',
'mode' => '',
]
)
) === 0;
$show_trash_delete_button = 'trash' === Params::get( 'post_status', false ) && have_posts() && current_user_can( get_post_type_object( $wp_list_table->screen->post_type )->cap->edit_others_posts );
include ADVADS_ABSPATH . 'views/admin/tables/placements/views-list.php';
}
/**
* Deprecate a catch-all action at the end of the placements list.
*
* @param string $which should be one of 'top' or 'bottom'.
*
* @return void
*/
public function placements_list_after( string $which ): void {
if ( 'bottom' !== $which ) {
return;
}
do_action_deprecated( 'advanced-ads-placements-list-after', [ 'placements' => false ], '1.48.0', '', 'Use the API for WP_List_Table.' );
}
/**
* Filter the row actions for placements.
*
* @param array $actions Array of actions.
*
* @return array
*/
public function row_actions( array $actions ): array {
// Remove quick edit.
unset( $actions['hide-if-no-js'], $actions['inline hide-if-no-js'] );
if ( $this->object->is_type( 'default' ) ) {
$actions['usage'] = '<a href="#modal-placement-usage-' . $this->object->get_id() . '" class="edits">' . esc_html__( 'Show Usage', 'advanced-ads' ) . '</a>';
}
return $actions;
}
/**
* Order ads by title on ads list
*
* @param array $query_vars Query vars.
*
* @return array
*/
protected function query_filters( $query_vars ): array {
// Early bail!!
if ( wp_doing_ajax() ) {
return $query_vars;
}
// Filter by type.
$placement_type = sanitize_text_field( Params::get( 'placement-type', '' ) );
if ( '' !== $placement_type ) {
$query_vars['meta_key'] = 'type'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
$query_vars['meta_value'] = $placement_type; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
}
// Sort by type.
$order = Params::get( 'order', 'asc' );
$orderby = Params::get( 'orderby', 'type' );
if ( in_array( $orderby, [ 'type' ], true ) ) {
$query_vars['order'] = $order;
if ( 'type' === $orderby ) {
$query_vars['meta_key'] = 'type'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
$query_vars['orderby'] = 'meta_value';
add_filter( 'posts_orderby', [ $this, 'sort_by_type_order' ], 10, 2 );
}
}
return $query_vars;
}
/**
* Set the ORDER BY clause of the query.
*
* @param string $order_sql The ORDER BY clause of the query.
* @param WP_Query $wp_query The current query instance.
*
* @return string
*/
public function sort_by_type_order( string $order_sql, WP_Query $wp_query ): string {
global $wpdb;
// Early bail!!
if ( ! $wp_query->is_main_query() ) {
return $order_sql;
}
$order = Params::get( 'order', 'asc' );
$types_order = wp_advads_get_placement_type_manager()->get_order_list();
$types_order = array_keys( $types_order );
$order_strings = [
$wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
sprintf(
'FIELD(%s.meta_value, %s ) %s',
$wpdb->postmeta,
implode( ', ', array_fill( 0, count( $types_order ), '%s' ) ),
$order // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
),
$types_order
),
"{$wpdb->posts}.post_title {$order}",
];
return implode( ', ', $order_strings );
}
/**
* Add placement type filter to the placements list.
*
* @return void
*/
public function render_filters(): void {
$current_type = Params::get( 'placement-type', '' );
include_once ADVADS_ABSPATH . 'views/admin/tables/placements/filter-types.php';
}
/**
* Render the Type column.
*
* @return void
*/
protected function render_type_column(): void {
$placement = $this->object;
( new Placement_Edit_Modal( $placement ) )->hooks();
$this->render_usage_modal();
require ADVADS_ABSPATH . 'views/admin/tables/placements/column-type.php';
}
/**
* Render the Name column.
*
* @return void
*/
protected function render_name_column(): void {
$placement = $this->object;
require ADVADS_ABSPATH . 'views/admin/tables/placements/column-name.php';
}
/**
* Render the Ad/Group column.
*
* @return void
*/
protected function render_ad_group_column(): void {
$placement = $this->object;
include ADVADS_ABSPATH . 'views/admin/tables/placements/column-ad-group.php';
if ( $this->object->is_type( 'header' ) ) {
include ADVADS_ABSPATH . 'views/admin/tables/placements/header-note.php';
}
}
/**
* Render the Conditions column.
*
* @return void
*/
protected function render_conditions_column(): void {
$placement = $this->object;
require ADVADS_ABSPATH . 'views/admin/tables/placements/column-conditions.php';
}
/**
* Render usage form modal
*
* @return void
*/
private function render_usage_modal(): void {
if ( ! $this->object->is_type( 'default' ) ) {
return;
}
ob_start();
$placement = $this->object;
include ADVADS_ABSPATH . 'views/admin/tables/placements/column-usage.php';
$modal_content = ob_get_clean();
Modal::create(
[
'modal_slug' => 'placement-usage-' . $placement->get_id(),
'modal_content' => $modal_content,
'modal_title' => __( 'Usage', 'advanced-ads' ),
'cancel_action' => false,
'close_action' => __( 'Close', 'advanced-ads' ),
]
);
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* Admin Placement Quick Edit.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Placement Quick Edit.
*/
class Placement_Quick_Edit implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
if ( ! Conditional::user_can( 'advanced_ads_manage_placements' ) ) {
return;
}
add_action( 'quick_edit_custom_box', [ $this, 'add_quick_edit_fields' ], 10, 2 );
add_action( 'save_post', [ $this, 'save_quick_edits' ], 100 );
add_action( 'admin_init', [ $this, 'enqueue_scripts' ] );
}
/**
* Add inline script
*
* @return void
*/
public function enqueue_scripts(): void {
wp_advads()->json->add(
'placements',
[
'read_nonce' => wp_create_nonce( 'advads-read-placement' ),
'draft' => __( 'Draft', 'advanced-ads' ),
]
);
}
/**
* Add fields to the quick edit form
*
* @param string $column currently processed column.
* @param string $post_type the post type.
*
* @return void
*/
public function add_quick_edit_fields( $column, $post_type ): void {
if ( Constants::POST_TYPE_PLACEMENT !== $post_type || 'type' !== $column ) {
return;
}
include plugin_dir_path( ADVADS_FILE ) . 'views/admin/placements/quick-edit.php';
}
/**
* Save quick edit data
*
* @param int $id the placement id.
*
* @return void
*/
public function save_quick_edits( $id ): void {
// Not inline edit, or no permission.
if ( ! wp_verify_nonce( sanitize_key( Params::post( '_inline_edit' ) ), 'inlineeditnonce' ) ) {
return;
}
$placement = wp_advads_get_placement( $id );
if ( ! $placement ) {
return;
}
$placement->set_status( Params::post( 'status', '', FILTER_UNSAFE_RAW ) );
$placement->save();
( new Placement_List_Table() )->hooks();
}
}

View File

@@ -0,0 +1,110 @@
<?php
// phpcs:ignoreFile
/**
* Alternative version installer.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use stdClass;
use WP_Error;
use Plugin_Upgrader;
use Automatic_Upgrader_Skin;
defined( 'ABSPATH' ) || exit;
/**
* Alternate plugin version installer
*/
class Plugin_Installer {
/**
* The version to install
*
* @var string
*/
private $version;
/**
* URL to the .zip archive for the desired version
*
* @var string
*/
private $package_url;
/**
* The plugin name
*
* @var string
*/
private $plugin_name;
/**
* The plugin slug
*
* @var string
*/
private $plugin_slug;
/**
* Constructor
*
* @param string $version The version to install.
* @param string $package_url The url to the .zip archive on https://wordpress.org.
*/
public function __construct( $version, $package_url ) {
$this->version = $version;
$this->package_url = $package_url;
$this->plugin_name = ADVADS_PLUGIN_BASENAME;
$this->plugin_slug = basename( ADVADS_FILE ) . '.php';
}
/**
* Apply package.
*
* Change the plugin data when WordPress checks for updates. This method
* modifies package data to update the plugin from a specific URL containing
* the version package.
*/
protected function apply_package() {
$update_plugins = get_site_transient( 'update_plugins' );
if ( ! is_object( $update_plugins ) ) {
$update_plugins = new stdClass();
}
$plugin_info = new stdClass();
$plugin_info->new_version = $this->version;
$plugin_info->slug = $this->plugin_slug;
$plugin_info->package = $this->package_url;
$update_plugins->response[ $this->plugin_name ] = $plugin_info;
set_site_transient( 'update_plugins', $update_plugins );
}
/**
* Do the plugin update process
*
* @return array|bool|WP_Error
*/
public function install() {
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
$this->apply_package();
$upgrader_args = [
'url' => 'update.php?action=upgrade-plugin&plugin=' . rawurlencode( $this->plugin_name ),
'plugin' => $this->plugin_name,
'nonce' => 'upgrade-plugin_' . $this->plugin_name,
'title' => esc_html__( 'Rollback to Previous Version', 'advanced-ads' ),
];
$upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin( $upgrader_args ) );
return $upgrader->upgrade( $this->plugin_name );
}
}

View File

@@ -0,0 +1,192 @@
<?php
/**
* Display ad-related information on the post and page overview page.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.x.x
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Class Post_List
*/
class Post_List implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'restrict_manage_posts', [ $this, 'add_ads_filter_dropdown' ] );
add_action( 'pre_get_posts', [ $this, 'filter_posts_by_ads_status' ] );
add_filter( 'manage_posts_columns', [ $this, 'ads_column_init' ] );
add_filter( 'manage_pages_columns', [ $this, 'ads_column_init' ] );
add_filter( 'manage_edit-post_sortable_columns', [ $this, 'sortable_ads_column' ] );
add_filter( 'manage_edit-page_sortable_columns', [ $this, 'sortable_ads_column' ] );
add_filter( 'posts_clauses', [ $this, 'request_clauses' ], 10, 2 );
add_action( 'manage_posts_custom_column', [ $this, 'ads_column_content' ], 10, 2 );
add_action( 'manage_pages_custom_column', [ $this, 'ads_column_content' ], 10, 2 );
add_filter( 'default_hidden_columns', [ $this, 'hide_ads_column_by_default' ], 10, 2 );
}
/**
* Add a filter dropdown to the post and pages lists.
*
* @param string $post_type current post type.
*
* @return void
*/
public function add_ads_filter_dropdown( string $post_type ): void {
if ( ! in_array( $post_type, [ 'post', 'page' ], true ) ) {
return;
}
$viewability = Params::get( 'ad-viewability', '' );
include ADVADS_ABSPATH . 'admin/views/post-list-filter-dropdown.php';
}
/**
* Filter the list of posts and pages based on their ads settings
*
* @param \WP_Query $query The WP_Query object.
*
* @return void
*/
public function filter_posts_by_ads_status( \WP_Query $query ): void {
if ( ! is_admin() || ! $query->is_main_query() || ! $query->get( 'post_type' ) || ! in_array( $query->get( 'post_type' ), [ 'post', 'page' ], true ) ) {
return;
}
$viewability = Params::get( 'ad-viewability', '' );
if ( ! $viewability ) {
return;
}
if ( in_array( $viewability, [ 'disable_ads', 'disable_the_content' ], true ) ) {
$query->set( 'meta_key', '_advads_ad_settings' );
$query->set( 'meta_compare', 'LIKE' );
$query->set( 'meta_value', '"' . $viewability . '";i:1;' );
}
}
/**
* Order post list by ad status
*
* @param array $clauses existing request clauses.
* @param \WP_Query $query the current WP Query.
*
* @return array
*/
public function request_clauses( $clauses, $query ) {
global $wpdb;
if ( empty( $query->query_vars['orderby'] ) || 'ad-status' !== $query->query_vars['orderby'] || ! $query->is_main_query() ) {
// No need to order by ad status.
return $clauses;
}
if ( ! function_exists( 'get_current_screen' ) ) {
return $clauses;
}
$screen = get_current_screen();
if ( ! $screen || ! in_array( $screen->id, [ 'edit-post', 'edit-page' ], true ) ) {
// Not the page we're interested in.
return $clauses;
}
// Create aliases for ads disabled on the post/page and injection into the content disabled.
$clauses['join'] .= " LEFT JOIN (SELECT post_id, IF(meta_value LIKE '%disable_ads\";i:1%', 1, 0) as ads, IF(meta_value LIKE '%disable_the_content\";s:1%', 1, 0) as content FROM {$wpdb->postmeta}"
. " WHERE meta_key = '_advads_ad_settings') as advads_meta on {$wpdb->posts}.ID = advads_meta.post_id";
$order = 'asc' === strtolower( $query->query_vars['order'] ) ? 'DESC' : 'ASC';
$clauses['orderby'] = "advads_meta.ads {$order}, advads_meta.content {$order}";
return $clauses;
}
/**
* Make the ad status column sortable
*
* @param array $columns columns list.
*
* @return array
*/
public function sortable_ads_column( $columns ) {
$columns['ad-status'] = 'ad-status';
return $columns;
}
/**
* Adds a new column to the post overview page for public post types.
*
* @param array $columns An array of column names.
*
* @return array The modified array of column names.
*/
public function ads_column_init( array $columns ): array {
$post_type = wp_doing_ajax() ? Params::post( 'post_type', '' ) : get_current_screen()->post_type;
if ( $post_type && get_post_type_object( $post_type )->public ) {
$columns['ad-status'] = __( 'Ad injection', 'advanced-ads' );
}
return $columns;
}
/**
* Displays the value of the "ads" post meta in the "Ads" column.
*
* @param string $column The name of the column.
* @param int $post_id The ID of the post.
*
* @return void
*/
public function ads_column_content( string $column, int $post_id ): void {
// Early bail!!
if ( 'ad-status' !== $column ) {
return;
}
$ads_post_meta = get_post_meta( $post_id, '_advads_ad_settings', true );
if ( ! empty( $ads_post_meta['disable_ads'] ) ) {
echo '<p>' . esc_html__( 'All ads disabled', 'advanced-ads' ) . '</p>';
}
if ( defined( 'AAP_VERSION' ) && ! empty( $ads_post_meta['disable_the_content'] ) ) {
echo '<p>' . esc_html__( 'Ads in content disabled', 'advanced-ads' ) . '</p>';
}
}
/**
* Hide the Ads column by default
*
* @param string[] $hidden hidden columns.
* @param \WP_Screen $screen screen object.
*
* @return string[]
*/
public function hide_ads_column_by_default( array $hidden, \WP_Screen $screen ): array {
$post_type_object = get_post_type_object( $screen->post_type );
if ( ! $post_type_object || ! $post_type_object->public ) {
return $hidden;
}
$hidden[] = 'ad-status';
return $hidden;
}
}

View File

@@ -0,0 +1,167 @@
<?php
/**
* The class is responsible for handling the edit posts views and some functionality on the edit post screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use stdClass;
use WP_Query;
use AdvancedAds\Constants;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Post Types.
*/
class Post_Types implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'post_updated_messages', [ $this, 'post_updated_messages' ] );
add_filter( 'bulk_post_updated_messages', [ $this, 'bulk_post_updated_messages' ], 10, 2 );
add_filter( 'wp_count_posts', [ $this, 'update_count_posts' ], 10, 2 );
add_filter( 'get_edit_post_link', [ $this, 'get_edit_post_link' ], 10, 2 );
}
/**
* Update post counts to have expiring ads.
*
* @param stdClass $counts An object containing the current post_type's post
* counts by status.
* @param string $type Post type.
*
* @return stdClass
*/
public function update_count_posts( $counts, $type ): stdClass {
if ( Constants::POST_TYPE_AD !== $type ) {
return $counts;
}
$query = new WP_Query(
[
'post_type' => Constants::POST_TYPE_AD,
'post_status' => 'any',
'fields' => 'ids',
'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
[
'key' => Constants::AD_META_EXPIRATION_TIME,
'value' => current_time( 'mysql', true ),
'compare' => '>=',
'type' => 'DATETIME',
],
],
]
);
$counts->{Constants::AD_STATUS_EXPIRING} = $query->found_posts;
return $counts;
}
/**
* Change messages when a post type is updated.
*
* @since 1.4.7
*
* @param array $messages Existing post update messages.
*
* @return array
*/
public function post_updated_messages( $messages = [] ): array {
global $post;
// Added to fix error message array caused by third party code that uses post_updated_messages filter wrong.
if ( ! is_array( $messages ) ) {
$messages = [];
}
$revision = Params::get( 'revision', 0, FILTER_VALIDATE_INT );
$messages[ Constants::POST_TYPE_AD ] = [
0 => '', // Unused. Messages start at index 1.
1 => __( 'Ad updated.', 'advanced-ads' ),
4 => __( 'Ad updated.', 'advanced-ads' ),
5 => $revision
/* translators: %s: date and time of the revision */
? sprintf( __( 'Ad restored to revision from %s', 'advanced-ads' ), wp_post_revision_title( $revision, false ) )
: false,
6 => __( 'Ad saved.', 'advanced-ads' ),
7 => __( 'Ad saved.', 'advanced-ads' ),
8 => __( 'Ad submitted.', 'advanced-ads' ),
9 => sprintf(
/* translators: %s: date */
__( 'Ad scheduled for: <strong>%1$s</strong>.', 'advanced-ads' ),
'<strong>' . date_i18n( __( 'M j, Y @ G:i', 'advanced-ads' ), strtotime( $post->post_date ) ) . '</strong>'
),
10 => __( 'Ad draft updated.', 'advanced-ads' ),
];
return $messages;
}
/**
* Edit ad bulk update messages
*
* @param array $messages existing bulk update messages.
* @param array $counts numbers of updated ads.
*
* @return array
*/
public function bulk_post_updated_messages( array $messages, array $counts ): array {
$messages[ Constants::POST_TYPE_AD ] = [
/* translators: %s: ad count */
'updated' => _n( '%s ad updated.', '%s ads updated.', $counts['updated'], 'advanced-ads' ),
/* translators: %s: ad count */
'locked' => _n( '%s ad not updated, somebody is editing it.', '%s ads not updated, somebody is editing them.', $counts['locked'], 'advanced-ads' ),
/* translators: %s: ad count */
'deleted' => _n( '%s ad permanently deleted.', '%s ads permanently deleted.', $counts['deleted'], 'advanced-ads' ),
/* translators: %s: ad count */
'trashed' => _n( '%s ad moved to the Trash.', '%s ads moved to the Trash.', $counts['trashed'], 'advanced-ads' ),
/* translators: %s: ad count */
'untrashed' => _n( '%s ad restored from the Trash.', '%s ads restored from the Trash.', $counts['untrashed'], 'advanced-ads' ),
];
$messages[ Constants::POST_TYPE_PLACEMENT ] = [
/* translators: %s: placement count */
'updated' => _n( '%s placement updated.', '%s placements updated.', $counts['updated'], 'advanced-ads' ),
/* translators: %s: placement count */
'locked' => _n( '%s placement not updated, somebody is editing it.', '%s placements not updated, somebody is editing them.', $counts['locked'], 'advanced-ads' ),
/* translators: %s: placement count */
'deleted' => _n( '%s placement permanently deleted.', '%s placements permanently deleted.', $counts['deleted'], 'advanced-ads' ),
/* translators: %s: placement count */
'trashed' => _n( '%s placement moved to the Trash.', '%s placements moved to the Trash.', $counts['trashed'], 'advanced-ads' ),
/* translators: %s: placement count */
'untrashed' => _n( '%s placement restored from the Trash.', '%s placements restored from the Trash.', $counts['untrashed'], 'advanced-ads' ),
];
return $messages;
}
/**
* Replace the edit link with a link to the modal to edit the placement.
*
* @param string $link The previous link.
* @param int $post_id The \WP_Post::$ID for the current item.
*
* @return string
*/
public function get_edit_post_link( string $link, int $post_id ): string {
if ( get_post_type( $post_id ) === Constants::POST_TYPE_PLACEMENT ) {
$link = admin_url( 'edit.php?post_type=' . Constants::POST_TYPE_PLACEMENT . '#modal-placement-edit-' . $post_id );
}
return $link;
}
}

View File

@@ -0,0 +1,375 @@
<?php
/**
* Add quick/bulk edit fields on the ad overview page
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.0
*/
namespace AdvancedAds\Admin;
use DateTime;
use Exception;
use Advanced_Ads_Privacy;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Utilities\Params;
/**
* WP integration
*/
class Quick_Bulk_Edit {
/**
* Hooks into WordPress
*
* @return void
*/
public function hooks() {
add_action( 'quick_edit_custom_box', [ $this, 'add_quick_edit_fields' ], 10, 2 );
add_action( 'bulk_edit_custom_box', [ $this, 'add_bulk_edit_fields' ], 10, 2 );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'save_post', [ $this, 'save_quick_edits' ], 100 );
add_action( 'save_post', [ $this, 'save_bulk_edit' ], 100 );
add_action( 'advanced-ads-ad-render-column-ad_type', [ $this, 'print_ad_json' ] );
}
/**
* Print ad JSON for debugging
*
* @param Ad $ad the ad being saved.
*
* @return void
*/
public function print_ad_json( $ad ): void {
?>
<script type="text/javascript">
var ad_json_<?php echo esc_attr( $ad->get_id() ); ?> = <?php echo wp_json_encode( $this->get_json_data( $ad ) ); ?>;
</script>
<?php
}
/**
* Save changes made during bulk edit
*
* @return void
*/
public function save_bulk_edit() {
// Not bulk edit, not ads or not enough permissions.
if (
! wp_verify_nonce( sanitize_key( Params::get( '_wpnonce', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ), 'bulk-posts' )
|| Constants::POST_TYPE_AD !== sanitize_key( Params::get( 'post_type' ) )
|| ! current_user_can( 'advanced_ads_edit_ads' )
) {
return;
}
$changes = [ 'on', 'off' ];
$debug_mode = Params::get( 'debug_mode' );
$set_expiry = Params::get( 'expiry_date' );
$ad_label = Params::get( 'ad_label', false );
$ignore_privacy = Params::get( 'ignore_privacy' );
$has_change = in_array( $debug_mode, $changes, true ) || in_array( $set_expiry, $changes, true ) || in_array( $ignore_privacy, $changes, true ) || false !== $ad_label;
/**
* Allow add-ons to confirm early abort if no change has been made and avoid iterating through an ad stack.
*
* @param bool $has_change whether some ads have been changed.
*/
$has_change = apply_filters( 'advanced-ads-bulk-edit-has-change', $has_change );
// No changes, bail out.
if ( ! $has_change ) {
return;
}
$expiry_date = 'on' === $set_expiry ?
$this->get_expiry_timestamp( 'get' ) : 0;
$ads = array_map(
function ( $ad ) {
return wp_advads_get_ad( absint( $ad ) );
},
wp_unslash( Params::get( 'post', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ) )
);
foreach ( $ads as $ad ) {
if ( in_array( $debug_mode, $changes, true ) ) {
$ad->set_debugmode( 'on' === $debug_mode );
}
if ( in_array( $set_expiry, $changes, true ) ) {
$ad->set_prop( 'expiry_date', $expiry_date );
}
if ( false !== $ad_label ) {
$ad->set_prop( 'ad_label', esc_html( trim( $ad_label ) ) );
}
if ( 'on' === $ignore_privacy ) {
$ad->set_prop( 'privacy', [ 'ignore-consent' => 'on' ] );
} elseif ( 'off' === $ignore_privacy ) {
$ad->unset_prop( 'privacy' );
}
/**
* Allow add-on to bulk save ads.
*
* @param Ad $ad current ad being saved.
*/
$ad = apply_filters( 'advanced-ads-bulk-edit-save', $ad );
$ad->save();
}
}
/**
* Save ad edited with quick edit
*
* @param int $id the ad being saved.
*
* @return void
*/
public function save_quick_edits( $id ) {
// Not inline edit, or no permission.
if (
! wp_verify_nonce( sanitize_key( Params::post( '_inline_edit' ) ), 'inlineeditnonce' ) ||
! current_user_can( 'advanced_ads_edit_ads' )
) {
return;
}
$ad = wp_advads_get_ad( $id );
// Not an ad.
if ( ! $ad ) {
return;
}
// Render columns properly.
( new Ad_List_Table() )->hooks();
$ad->set_prop( 'debugmode', Params::post( 'debugmode', false, FILTER_VALIDATE_BOOLEAN ) );
$ad->set_prop(
'expiry_date',
Params::post( 'enable_expiry' ) ? $this->get_expiry_timestamp() : 0
);
if ( isset( Advanced_Ads_Privacy::get_instance()->options()['enabled'] ) ) {
if ( Params::post( 'ignore_privacy' ) ) {
$ad->set_prop( 'privacy', [ 'ignore-consent' => 'on' ] );
} else {
$ad->unset_prop( 'privacy' );
}
}
$ad_label = Params::post( 'ad_label', false );
if ( false !== $ad_label ) {
$ad->set_prop( 'ad_label', esc_html( trim( $ad_label ) ) );
}
/**
* Allow add-ons to edit and ad before it is saved.
*
* @param Ad $ad the ad being saved.
*/
$ad = apply_filters( 'advanced-ads-quick-edit-save', $ad );
$ad->save();
}
/**
* Get Unix timestamp from the date time inputs values
*
* @param string $method method used for the form - `post` or `get`.
*
* @return int
*/
private function get_expiry_timestamp( $method = 'post' ) {
$day = absint( 'get' === $method ? Params::get( 'day' ) : Params::post( 'day' ) );
$month = absint( 'get' === $method ? Params::get( 'month' ) : Params::post( 'month' ) );
$year = 'get' === $method ? Params::get( 'year', 0, FILTER_VALIDATE_INT ) : Params::post( 'year', 0, FILTER_VALIDATE_INT );
$hours = absint( 'get' === $method ? Params::get( 'hour' ) : Params::post( 'hour' ) );
$minutes = absint( 'get' === $method ? Params::get( 'minute' ) : Params::post( 'minute' ) );
try {
$local_dt = new \DateTimeImmutable( 'now', WordPress::get_timezone() );
$local_dt = $local_dt->setDate( $year, $month, $day )->setTime( $hours, $minutes );
return $local_dt->getTimestamp();
} catch ( Exception $e ) {
return 0;
}
}
/**
* Enqueue scripts and print inline JS variable.
*
* @return void
*/
public function enqueue_scripts() {
$screen = get_current_screen();
if ( 'edit-advanced_ads' !== $screen->id ) {
return;
}
wp_advads()->registry->enqueue_script( 'screen-ads-listing' );
}
/**
* Add the bulk edit inputs
*
* @param string $column_name the current column.
* @param string $post_type the current post type.
*
* @return void
*/
public function add_bulk_edit_fields( $column_name, $post_type ) {
if ( Constants::POST_TYPE_AD !== $post_type || 'ad_type' !== $column_name ) {
return;
}
$privacy_options = \Advanced_Ads_Privacy::get_instance()->options();
include plugin_dir_path( ADVADS_FILE ) . 'views/admin/bulk-edit.php';
/**
* Allow add-ons to add more fields.
*/
do_action( 'advanced-ads-bulk-edit-fields' );
}
/**
* Add the quick edit inputs
*
* @param string $column_name the current column.
* @param string $post_type the current post type.
*
* @return void
*/
public function add_quick_edit_fields( $column_name, $post_type ) {
if ( Constants::POST_TYPE_AD !== $post_type || 'ad_date' !== $column_name ) {
return;
}
$privacy_options = \Advanced_Ads_Privacy::get_instance()->options();
include plugin_dir_path( ADVADS_FILE ) . 'views/admin/quick-edit.php';
/**
* Allow add-ons to add more fields.
*/
do_action( 'advanced-ads-quick-edit-fields' );
}
/**
* Print date and time inputs for the ad expiry
*
* @param int $timestamp default expiry date.
* @param string $prefix prefix for input names.
* @param bool $seconds whether to add seconds input.
*
* @return void
*/
public static function print_date_time_inputs( $timestamp = 0, $prefix = '', $seconds = false ) {
try {
$initial_date = (bool) $timestamp ? new \DateTimeImmutable( "@$timestamp", new \DateTimeZone( 'UTC' ) ) : current_datetime();
} catch ( Exception $e ) {
$initial_date = current_datetime();
}
$current_year = (int) ( current_datetime()->format( 'Y' ) );
global $wp_locale;
?>
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Month', 'advanced-ads' ); ?></span>
<select name="<?php echo esc_attr( $prefix ); ?>month">
<?php for ( $mo = 1; $mo < 13; $mo++ ) : ?>
<?php $month = zeroise( $mo, 2 ); ?>
<option value="<?php echo esc_attr( $month ); ?>" <?php selected( $month, $initial_date->format( 'm' ) ); ?>>
<?php echo esc_html( $month . '-' . $wp_locale->get_month_abbrev( $wp_locale->get_month( $mo, 2 ) ) ); ?>
</option>
<?php endfor; ?>
</select>
</label>
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Day', 'advanced-ads' ); ?></span>
<input type="number" name="<?php echo esc_attr( $prefix ); ?>day" min="1" max="31" value="<?php echo esc_attr( $initial_date->format( 'd' ) ); ?>"/>
</label>,
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Year', 'advanced-ads' ); ?></span>
<select name="<?php echo esc_attr( $prefix ); ?>year">
<?php for ( $y = $current_year; $y < $current_year + 11; $y++ ) : ?>
<option value="<?php echo esc_attr( $y ); ?>" <?php selected( $y, (int) $initial_date->format( 'Y' ) ); ?>><?php echo esc_html( $y ); ?></option>
<?php endfor; ?>
</select>
</label>
@
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Hour', 'advanced-ads' ); ?></span>
<input type="number" name="<?php echo esc_attr( $prefix ); ?>hour" min="0" max="23" value="<?php echo esc_attr( $initial_date->format( 'H' ) ); ?>"/>
</label>:
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Minute', 'advanced-ads' ); ?></span>
<input type="number" name="<?php echo esc_attr( $prefix ); ?>minute" min="0" max="59" value="<?php echo esc_attr( $initial_date->format( 'i' ) ); ?>"/>
</label>
<?php if ( $seconds ) : ?>
:
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Second', 'advanced-ads' ); ?></span>
<input type="number" name="<?php echo esc_attr( $prefix ); ?>second" min="0" max="59" value="<?php echo esc_attr( $initial_date->format( 's' ) ); ?>"/>
</label>
<?php endif; ?>
<?php $timezone = wp_timezone_string(); ?>
<span><?php echo esc_html( strlen( $timezone ) !== strlen( str_replace( [ '+', '-' ], '', $timezone ) ) ? "UTC$timezone" : $timezone ); ?></span>
<?php
}
/**
* Get ad data for json output
*
* @param Ad $ad Ad instance.
*
* @return array
*/
private function get_json_data( $ad ): array {
$expiry = $ad->get_expiry_date();
if ( $expiry ) {
$expiry_date = array_combine(
[ 'year', 'month', 'day', 'hour', 'minute' ],
explode( '-', wp_date( 'Y-m-d-H-i', $expiry ) )
);
}
$ad_data = [
'debug_mode' => $ad->is_debug_mode(),
'expiry' => $expiry
? [
'expires' => true,
'expiry_date' => $expiry_date,
]
: [
'expires' => false,
],
'ad_label' => $ad->get_prop( 'ad_label' ),
];
if ( isset( Advanced_Ads_Privacy::get_instance()->options()['enabled'] ) ) {
$ad_data['ignore_privacy'] = isset( $ad->get_data()['privacy']['ignore-consent'] );
}
/**
* Allow add-ons to add more ad data fields.
*
* @param array $ad_data the fields to be sent back to the browser.
* @param $ad Ad the ad being currently edited.
*/
$ad_data = apply_filters( 'advanced-ads-quick-edit-ad-data', $ad_data, $ad );
return $ad_data;
}
}

View File

@@ -0,0 +1,191 @@
<?php
/**
* Admin Screen Options.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use WP_Screen;
use AdvancedAds\Options;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Screen Options.
*/
class Screen_Options implements Integration_Interface {
const USER_META_KEY = 'advanced-ads-screen-options';
/**
* Array key for screen options.
*
* @var string
*/
private $screen_key = '';
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'screen_settings', [ $this, 'add_screen_options' ], 10, 2 );
add_action( 'wp_loaded', [ $this, 'save_screen_options' ] );
add_action( 'load-edit.php', [ $this, 'set_screen_options' ] );
}
/**
* Return true if the current screen is the ad or placement list.
*
* @return bool
*/
private function is_screen(): bool {
return Conditional::is_screen( [ 'edit-advanced_ads', 'edit-advanced_ads_plcmnt' ] );
}
/**
* Register custom screen options on the ad overview page.
*
* @param string $options Screen options HTML.
* @param WP_Screen $screen Screen object.
*
* @return string
*/
public function add_screen_options( $options, WP_Screen $screen ) {
if ( ! $this->is_screen() ) {
return $options;
}
$selected_filters = $screen->get_option( 'filters_to_show' ) ?? [];
$is_filter_permanent = boolval( $screen->get_option( 'show-filters' ) );
$optional_filters = $this->get_optional_filters();
// If the default WordPress screen options don't exist, we have to force the submit button to show.
add_filter( 'screen_options_show_submit', '__return_true' );
ob_start();
require ADVADS_ABSPATH . 'views/admin/screen-options.php';
return $options . ob_get_clean();
}
/**
* Add the screen options to the WP_Screen options
*
* @return void
*/
public function set_screen_options(): void {
$screen_options = $this->get_screen_options();
// Early bail!!
if ( ! $this->is_screen() || empty( $screen_options ) ) {
return;
}
$screen_key = $this->get_screen_key( get_current_screen()->id );
$screen_options = $screen_options[ $screen_key ] ?? [];
foreach ( $screen_options as $option_name => $value ) {
add_screen_option( $option_name, $value );
}
}
/**
* Save the screen option setting.
*
* @return void
*/
public function save_screen_options() {
$options = Params::post( self::USER_META_KEY, false, FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$user = wp_get_current_user();
// Early bail!!
if ( ! $options || ! $user ) {
return;
}
check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' );
$screen_options = $this->get_screen_options();
$screen_key = $this->get_screen_key( $options['screen-id'] );
// no need to save it.
unset( $options['screen-id'] );
$screen_options[ $screen_key ] = $options;
update_user_meta( $user->ID, self::USER_META_KEY, $screen_options );
}
/**
* Get the current user screen options from DB.
*
* @return array
*/
private function get_screen_options() {
$screen_options = get_user_meta( get_current_user_id(), self::USER_META_KEY, true );
if ( ! is_array( $screen_options ) ) {
return [];
}
return $screen_options;
}
/**
* Get the screen key for DB use.
*
* @param string $screen_id Screen ID.
*
* @return string
*/
private function get_screen_key( $screen_id = false ) {
if ( ! $screen_id && ! empty( $this->screen_key ) ) {
return $this->screen_key;
}
switch ( $screen_id ) {
case 'edit-advanced_ads':
$this->screen_key = 'ad';
break;
case 'edit-advanced_ads_plcmnt':
$this->screen_key = 'placement';
break;
default:
$this->screen_key = false;
}
return $this->screen_key;
}
/**
* Get optional filters.
*
* @return array The optional filters.
*/
private function get_optional_filters() {
// $optional_filters array order determines display sequence.
$optional_filters = [];
if ( Conditional::is_screen( [ 'edit-advanced_ads' ] ) ) {
$optional_filters ['all_debug_mode'] = __( 'Debug Mode', 'advanced-ads' );
$optional_filters['all_authors'] = __( 'Author', 'advanced-ads' );
// show only when privacy setting is enabled.
if ( Options::instance()->get( 'privacy.enabled' ) ) {
$optional_filters['all_privacyignore'] = __( 'Privacy Ignore', 'advanced-ads' );
}
$optional_filters = apply_filters( 'advanced_ads_optional_filters', $optional_filters );
}
return $optional_filters;
}
}

View File

@@ -0,0 +1,703 @@
<?php
/**
* Setting class
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Admin;
defined( 'ABSPATH' ) || exit;
use Advanced_Ads;
use Advanced_Ads_Utils;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Data;
use AdvancedAds\Utilities\Sanitize;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
/**
* Class Settings
*/
class Settings implements Integration_Interface {
/**
* Settings page slug
*
* @var string
*/
const ADVADS_SETTINGS_LICENSES = ADVADS_SLUG . '-licenses';
/**
* Setting options
*
* @var array with plugin options
*/
private $options;
/**
* Hook name
*
* @var string
*/
private $hook;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_init', [ $this, 'settings_init' ] );
add_action( 'admin_init', [ $this, 'settings_capabilities' ], 20 );
add_filter( 'advanced-ads-setting-tabs', [ $this, 'add_tabs' ], 50 );
add_filter( 'advanced-ads-ad-admin-options', [ $this, 'allow_save_settings' ] );
$this->options = Advanced_Ads::get_instance()->options();
}
/**
* Add tabs to the settings page.
*
* @param array $tabs setting tabs.
*
* @return array
*/
public function add_tabs( array $tabs ): array {
if ( ! defined( 'AAP_VERSION' ) ) {
$tabs['pro_pitch'] = [
'page' => 'advanced-ads-settings-pro-pitch-page',
'tabid' => 'pro-pitch',
'title' => __( 'Pro', 'advanced-ads' ),
];
}
if ( ! defined( 'AAT_VERSION' ) ) {
$tabs['tracking_pitch'] = [
'page' => 'advanced-ads-settings-tracking-pitch-page',
'tabid' => 'tracking-pitch',
'title' => __( 'Tracking', 'advanced-ads' ),
];
}
$tabs['licenses'] = [
'page' => 'advanced-ads-settings-license-page',
'group' => self::ADVADS_SETTINGS_LICENSES,
'tabid' => 'licenses',
'title' => __( 'Licenses', 'advanced-ads' ),
];
return $tabs;
}
/**
* Initialize settings
*
* @since 1.0.1
*
* @return void
*/
public function settings_init(): void {
$this->hook = wp_advads()->screens->get_hook( 'settings' );
register_setting( ADVADS_SLUG, ADVADS_SLUG, [ $this, 'sanitize_settings' ] );
register_setting( self::ADVADS_SETTINGS_LICENSES, self::ADVADS_SETTINGS_LICENSES );
$this->section_management();
$this->section_disable_ads();
$this->section_layout();
$this->section_content_injection();
$this->section_pro_pitches();
$this->section_licenses();
// hook for additional settings from add-ons.
do_action( 'advanced-ads-settings-init', $this->hook );
}
/**
* Make sure ad admin can save options.
* Add a filter on `admin_init` priority 20 to allow other modules/add-ons to add their options.
* Filter option_page_capability_ with the appropriate slug in return to allow the Ad Admin user role to save these settings/options.
*/
public function settings_capabilities() {
$ad_admin_options = [ ADVADS_SLUG ];
/**
* Filters all options that the Ad Admin Role should have access to.
*
* @param array $ad_admin_options Array with option names.
*/
$ad_admin_options = apply_filters( 'advanced-ads-ad-admin-options', $ad_admin_options );
foreach ( $ad_admin_options as $ad_admin_option ) {
add_filter(
'option_page_capability_' . $ad_admin_option,
function () {
return Conditional::user_cap( 'advanced_ads_manage_options' );
}
);
}
}
/**
* Allow Ad Admin to save settings.
*
* @param string[] $options Array with allowed options.
*
* @return string[]
*/
public function allow_save_settings( $options ) {
$options[] = ADVADS_SETTINGS_ADBLOCKER;
$options[] = self::ADVADS_SETTINGS_LICENSES;
return $options;
}
/**
* Options to disable ads
*/
public function render_settings_disable_ads() {
$options = Advanced_Ads::get_instance()->options();
// set the variables.
$disable_all = isset( $options['disabled-ads']['all'] ) ? 1 : 0;
$disable_404 = isset( $options['disabled-ads']['404'] ) ? 1 : 0;
$disable_archives = isset( $options['disabled-ads']['archives'] ) ? 1 : 0;
$disable_secondary = isset( $options['disabled-ads']['secondary'] ) ? 1 : 0;
$disable_feed = ( ! isset( $options['disabled-ads']['feed'] ) || $options['disabled-ads']['feed'] ) ? 1 : 0;
$disable_rest_api = isset( $options['disabled-ads']['rest-api'] ) ? 1 : 0;
// load the template.
include_once ADVADS_ABSPATH . 'views/admin/settings/general/disable-ads.php';
}
/**
* Render setting to hide ads from logged in users
*/
public function render_settings_hide_for_users() {
global $wp_roles;
$hide_for_roles = [];
$options = Advanced_Ads::get_instance()->options();
$roles = $wp_roles->get_names();
if ( isset( $options['hide-for-user-role'] ) ) {
$hide_for_roles = Advanced_Ads_Utils::maybe_translate_cap_to_role( $options['hide-for-user-role'] );
}
include_once ADVADS_ABSPATH . 'views/admin/settings/general/hide-for-user-role.php';
}
/**
* Render setting to hide ads from logged in users
*/
public function render_settings_hide_for_ip_address() {
$disable_ip_addr = $this->options['hide-for-ip-address']['enabled'] ?? 0;
$ip_address = $this->options['hide-for-ip-address']['ips'] ?? '';
include_once ADVADS_ABSPATH . 'views/admin/settings/general/hide-for-ip-address.php';
}
/**
* Render setting to display advanced js file
*/
public function render_settings_advanced_js() {
$options = Advanced_Ads::get_instance()->options();
$checked = ( ! empty( $options['advanced-js'] ) ) ? 1 : 0;
include_once ADVADS_ABSPATH . 'views/admin/settings/general/advanced-js.php';
}
/**
* Render setting for content injection protection
*/
public function render_settings_content_injection_everywhere() {
$options = Advanced_Ads::get_instance()->options();
$enabled = $options['content-injection-enabled'] ?? '';
if ( ! isset( $options['content-injection-everywhere'] ) ) {
$everywhere = 0;
} elseif ( 'true' === $options['content-injection-everywhere'] ) {
$everywhere = - 1;
} else {
$everywhere = absint( $options['content-injection-everywhere'] );
}
include_once ADVADS_ABSPATH . 'views/admin/settings/general/content-injection-everywhere.php';
}
/**
* Render setting for content injection priority
*/
public function render_settings_content_injection_priority() {
$options = Advanced_Ads::get_instance()->options();
$priority = ( isset( $options['content-injection-priority'] ) ) ? (int) $options['content-injection-priority'] : 100;
include_once ADVADS_ABSPATH . 'views/admin/settings/general/content-injection-priority.php';
}
/**
* Render setting to disable content injection level limitation
*/
public function render_settings_content_injection_level_limitation() {
$options = Advanced_Ads::get_instance()->options();
$checked = ( ! empty( $options['content-injection-level-disabled'] ) ) ? 1 : 0;
include_once ADVADS_ABSPATH . 'views/admin/settings/general/content-injection-level-limitation.php';
}
/**
* Render setting for blocking bots
*/
public function render_settings_block_bots() {
$options = Advanced_Ads::get_instance()->options();
$checked = ( ! empty( $options['block-bots'] ) ) ? 1 : 0;
include_once ADVADS_ABSPATH . 'views/admin/settings/general/block-bots.php';
}
/**
* Render setting to disable ads by post types
*/
public function render_settings_disable_post_types() {
$post_types = get_post_types(
[
'public' => true,
'publicly_queryable' => true,
],
'objects',
'or'
);
$type_label_counts = array_count_values( wp_list_pluck( $post_types, 'label' ) );
include_once ADVADS_ABSPATH . '/views/admin/settings/general/disable-post-types.php';
}
/**
* Render setting to disable notices and Ad Health
*/
public function render_settings_disabled_notices() {
$options = Advanced_Ads::get_instance()->options();
$checked = ( ! empty( $options['disable-notices'] ) ) ? 1 : 0;
include_once ADVADS_ABSPATH . '/views/admin/settings/general/disable-notices.php';
}
/**
* Render setting for frontend prefix
*/
public function render_settings_front_prefix() {
$options = Advanced_Ads::get_instance()->options();
$prefix = wp_advads()->get_frontend_prefix();
$old_prefix = ( isset( $options['id-prefix'] ) ) ? esc_attr( $options['id-prefix'] ) : '';
include_once ADVADS_ABSPATH . '/views/admin/settings/general/frontend-prefix.php';
}
/**
* Render setting to allow editors to manage ads
*/
public function render_settings_editors_manage_ads() {
$allow = false;
$options = Advanced_Ads::get_instance()->options();
// is false by default if no options where previously set.
if ( isset( $options['editors-manage-ads'] ) && $options['editors-manage-ads'] ) {
$allow = true;
}
include_once ADVADS_ABSPATH . '/views/admin/settings/general/editors-manage-ads.php';
}
/**
* Prepare the template for multisite allow unfiltered_html settings.
*
* @return void
*/
public function renders_settings_allow_unfiltered_html() {
$options = Advanced_Ads::get_instance()->options();
$user_roles_to_display = Data::get_filtered_roles_by_cap();
if ( empty( $user_roles_to_display ) ) {
return;
}
if ( ! isset( $options['allow-unfiltered-html'] ) ) {
$options['allow-unfiltered-html'] = [];
}
$allowed_roles = $options['allow-unfiltered-html'];
include_once ADVADS_ABSPATH . '/views/admin/settings/general/allow-unfiltered-html.php';
}
/**
* Render setting to add an "Advertisement" label before ads
*/
public function render_settings_add_custom_label() {
$options = Advanced_Ads::get_instance()->options();
$enabled = isset( $options['custom-label']['enabled'] );
$label = ! empty( $options['custom-label']['text'] ) ? esc_html( $options['custom-label']['text'] ) : _x( 'Advertisements', 'label before ads', 'advanced-ads' );
$html_enabled = $options['custom-label']['html_enabled'] ?? false;
include_once ADVADS_ABSPATH . '/views/admin/settings/general/custom-label.php';
}
/**
* Render link target="_blank" setting
*
* @since 1.8.4 moved here from Tracking add-on
*/
public function render_settings_link_target_callback() {
// get option if saved for tracking.
$options = Advanced_Ads::get_instance()->options();
if ( ! isset( $options['target-blank'] ) && function_exists( 'wp_advads_tracking' ) ) {
$tracking_target = wp_advads_tracking()->options->get( 'target' );
if ( $tracking_target ) {
$options['target-blank'] = $tracking_target;
}
}
$target = isset( $options['target-blank'] ) ? $options['target-blank'] : 0;
include_once ADVADS_ABSPATH . 'views/admin/settings/general/link-target.php';
}
/**
* Render setting 'Delete data on uninstall"
*/
public function render_settings_uninstall_delete_data() {
$options = Advanced_Ads::get_instance()->options();
$enabled = ! empty( $options['uninstall-delete-data'] );
include_once ADVADS_ABSPATH . 'views/admin/settings/general/uninstall-delete-data.php';
}
/**
* Sanitize plugin settings
*
* @param array $options all the options.
*
* @return array sanitized options.
*/
public function sanitize_settings( $options ) {
if ( isset( $options['front-prefix'] ) ) {
$options['front-prefix'] = Sanitize::frontend_prefix(
$options['front-prefix'],
Constants::DEFAULT_FRONTEND_PREFIX
);
}
$options = apply_filters( 'advanced-ads-sanitize-settings', $options );
// check if editors can edit ads now and set the rights
// else, remove that right.
$editor_role = get_role( 'editor' );
if ( null === $editor_role ) {
return $options;
}
$action = isset( $options['editors-manage-ads'] ) && $options['editors-manage-ads']
? 'add_cap' : 'remove_cap';
$editor_role->$action( 'advanced_ads_see_interface' );
$editor_role->$action( 'advanced_ads_edit_ads' );
$editor_role->$action( 'advanced_ads_manage_placements' );
$editor_role->$action( 'advanced_ads_place_ads' );
// we need 3 states: ! empty, 1, 0.
$options['disabled-ads']['feed'] = ! empty( $options['disabled-ads']['feed'] ) ? 1 : 0;
if ( isset( $options['content-injection-everywhere'] ) ) {
if ( '0' === $options['content-injection-everywhere'] ) {
unset( $options['content-injection-everywhere'] );
} elseif ( 'true' === $options['content-injection-everywhere'] || $options['content-injection-everywhere'] <= - 1 ) {
// Note: the option may be already set 'true' during import.
$options['content-injection-everywhere'] = 'true';
} else {
$options['content-injection-everywhere'] = absint( $options['content-injection-everywhere'] );
}
}
return $options;
}
/**
* Add management section
*
* @return void
*/
private function section_management(): void {
$section_id = 'advanced_ads_setting_section';
add_settings_section(
$section_id,
__( 'Admin', 'advanced-ads' ),
'__return_empty_string',
$this->hook
);
add_settings_field(
'disable-notices',
__( 'Disable Ad Health and other notices', 'advanced-ads' ),
[ $this, 'render_settings_disabled_notices' ],
$this->hook,
$section_id
);
add_settings_field(
'editors-manage-ads',
__( 'Allow editors to manage ads', 'advanced-ads' ),
[ $this, 'render_settings_editors_manage_ads' ],
$this->hook,
$section_id
);
if (
is_multisite()
// Allow superadmins to edit the setting when DISALLOW_UNFILTERED_HTML is defined.
&& ( current_user_can( 'unfiltered_html' ) || is_super_admin() )
) {
add_settings_field(
'allow-unfiltered-html',
/* translators: unfiltered_html */
sprintf( __( 'Add the %s capability to user roles on multisite', 'advanced-ads' ), '<code>unfiltered_html</code>' ),
[ $this, 'renders_settings_allow_unfiltered_html' ],
$this->hook,
$section_id
);
}
if ( is_main_site() ) {
add_settings_field(
'uninstall-delete-data',
__( 'Delete data on uninstall', 'advanced-ads' ),
[ $this, 'render_settings_uninstall_delete_data' ],
$this->hook,
$section_id
);
}
}
/**
* Disable ads section
*
* @return void
*/
private function section_disable_ads(): void {
$section_id = 'advanced_ads_setting_section_disable_ads';
add_settings_section(
$section_id,
__( 'Disable ads', 'advanced-ads' ),
'__return_empty_string',
$this->hook
);
add_settings_field(
'disable-ads',
__( 'Disable ads', 'advanced-ads' ),
[ $this, 'render_settings_disable_ads' ],
$this->hook,
$section_id
);
add_settings_field(
'hide-for-user-role',
__( 'Hide ads for user roles', 'advanced-ads' ),
[ $this, 'render_settings_hide_for_users' ],
$this->hook,
$section_id
);
add_settings_field(
'hide-for-ip-address',
__( 'Hide ads for IP addresses', 'advanced-ads' ),
[ $this, 'render_settings_hide_for_ip_address' ],
$this->hook,
$section_id
);
add_settings_field(
'block-bots',
__( 'Hide ads from bots', 'advanced-ads' ),
[ $this, 'render_settings_block_bots' ],
$this->hook,
$section_id
);
if ( ! defined( 'AAP_VERSION' ) ) {
add_settings_field(
'disable-by-post-types-pro',
__( 'Disable ads for post types', 'advanced-ads' ),
[ $this, 'render_settings_disable_post_types' ],
$this->hook,
$section_id
);
}
}
/**
* Layout section
*
* @return void
*/
private function section_layout(): void {
$section_id = 'advanced_ads_setting_section_output';
add_settings_section(
$section_id,
__( 'Layout / Output', 'advanced-ads' ),
'__return_empty_string',
$this->hook
);
add_settings_field(
'front-prefix',
__( 'ID prefix', 'advanced-ads' ),
[ $this, 'render_settings_front_prefix' ],
$this->hook,
$section_id
);
add_settings_field(
'add-custom-label',
__( 'Ad label', 'advanced-ads' ),
[ $this, 'render_settings_add_custom_label' ],
$this->hook,
$section_id
);
add_settings_field(
'link-target',
__( 'Open links in a new window', 'advanced-ads' ),
[ $this, 'render_settings_link_target_callback' ],
$this->hook,
$section_id
);
add_settings_field(
'activate-advanced-js',
__( 'Use advanced JavaScript', 'advanced-ads' ),
[ $this, 'render_settings_advanced_js' ],
$this->hook,
$section_id
);
}
/**
* Content injection section
*
* @return void
*/
private function section_content_injection(): void {
$section_id = 'advanced_ads_setting_section_injection';
add_settings_section(
$section_id,
__( 'Content injection', 'advanced-ads' ),
'__return_empty_string',
$this->hook
);
add_settings_field(
'content-injection-everywhere',
__( 'Content placement in post lists', 'advanced-ads' ),
[ $this, 'render_settings_content_injection_everywhere' ],
$this->hook,
$section_id
);
add_settings_field(
'content-injection-priority',
__( 'Priority of content injection filter', 'advanced-ads' ),
[ $this, 'render_settings_content_injection_priority' ],
$this->hook,
$section_id
);
add_settings_field(
'content-injection-level-limitation',
__( 'Disable level limitation', 'advanced-ads' ),
[ $this, 'render_settings_content_injection_level_limitation' ],
$this->hook,
$section_id
);
}
/**
* Pro pitches section
*
* @return void
*/
private function section_pro_pitches(): void {
// Pro pitch section.
if ( ! defined( 'AAP_VERSION' ) ) {
add_settings_section(
'advanced_ads_settings_pro_pitch_section',
'',
[ $this, 'render_settings_pro_pitch_section_callback' ],
'advanced-ads-settings-pro-pitch-page'
);
}
// Tracking pitch section.
if ( ! defined( 'AAT_VERSION' ) ) {
add_settings_section(
'advanced_ads_settings_tracking_pitch_section',
'',
[ $this, 'render_settings_tracking_pitch_section_callback' ],
'advanced-ads-settings-tracking-pitch-page'
);
}
}
/**
* Render pitch for Pro
*
* @return void
*/
public function render_settings_pro_pitch_section_callback() {
echo '<br/>';
include ADVADS_ABSPATH . 'views/admin/upgrades/pro-tab.php';
}
/**
* Render tracking pitch settings section
*/
public function render_settings_tracking_pitch_section_callback() {
echo '<br/>';
include ADVADS_ABSPATH . 'views/marketing/ad-metabox-tracking.php';
}
/**
* Licenses section
*
* @return void
*/
private function section_licenses(): void {
add_settings_section(
'advanced_ads_settings_license_section',
'',
[ $this, 'render_settings_licenses_section_callback' ],
'advanced-ads-settings-license-page'
);
add_settings_section(
'advanced_ads_settings_license_pitch_section',
'',
[ $this, 'render_settings_licenses_pitch_section_callback' ],
'advanced-ads-settings-license-page'
);
}
/**
* Render licenses settings section
*/
public function render_settings_licenses_section_callback() {
include ADVADS_ABSPATH . 'views/admin/settings/license/section-help.php';
}
/**
* Render licenses pithces settings section
*/
public function render_settings_licenses_pitch_section_callback() {
echo '<h3>' . esc_attr__( 'Are you missing something?', 'advanced-ads' ) . '</h3>';
\Advanced_Ads_Overview_Widgets_Callbacks::render_addons( true, false );
}
}

View File

@@ -0,0 +1,202 @@
<?php
/**
* Admin Shortcode Creator.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use _WP_Editors;
use AdvancedAds\Utilities\Data;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Shortcode Creator.
*/
class Shortcode_Creator implements Integration_Interface {
/**
* Contains ids of the editors that contains the Advanced Ads button.
*
* @var array
*/
private $editors = [];
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
if (
true !== boolval( get_user_option( 'rich_editing' ) ) ||
! Conditional::user_can( 'advanced_ads_place_ads' ) ||
defined( 'ADVANCED_ADS_DISABLE_SHORTCODE_BUTTON' ) ||
apply_filters( 'advanced-ads-disable-shortcode-button', false )
) {
return;
}
add_filter( 'mce_buttons', [ $this, 'register_buttons' ], 10, 2 );
add_filter( 'tiny_mce_plugins', [ $this, 'register_plugin' ] );
add_filter( 'tiny_mce_before_init', [ $this, 'tiny_mce_before_init' ], 10, 2 );
add_action( 'wp_tiny_mce_init', [ $this, 'print_shortcode_plugin' ] );
add_action( 'print_default_editor_scripts', [ $this, 'print_shortcode_plugin' ] );
add_action( 'wp_ajax_advads_content_for_shortcode_creator', [ $this, 'get_content_for_shortcode_creator' ] );
}
/**
* Add button to tinyMCE window
*
* @param array $buttons Array with existing buttons.
* @param string $editor_id Unique editor identifier.
*
* @return array
*/
public function register_buttons( $buttons, $editor_id ): array {
if ( ! $this->hooks_exist() ) {
return $buttons;
}
if ( ! is_array( $buttons ) ) {
$buttons = [];
}
$this->editors[] = $editor_id;
$buttons[] = 'advads_shortcode_button';
return $buttons;
}
/**
* Add the plugin to the array of default TinyMCE plugins.
*
* @param array $plugins An array of default TinyMCE plugins.
*
* @return array
*/
public function register_plugin( $plugins ): array {
if ( ! $this->hooks_exist() ) {
return $plugins;
}
$plugins[] = 'advads_shortcode';
return $plugins;
}
/**
* Delete the plugin added by the {@see `tiny_mce_plugins`} method when necessary hooks do not exist.
*
* @param array $mce_init An array with TinyMCE config.
* @param string $editor_id Unique editor identifier.
*
* @return array
*/
public function tiny_mce_before_init( $mce_init, $editor_id = '' ): array {
// Early bail!!
if ( ! isset( $mce_init['plugins'] ) || ! is_string( $mce_init['plugins'] ) ) {
return $mce_init;
}
$plugins = explode( ',', $mce_init['plugins'] );
$found = array_search( 'advads_shortcode', $plugins, true );
if ( ! $found || ( '' !== $editor_id && in_array( $editor_id, $this->editors, true ) ) ) {
return $mce_init;
}
unset( $plugins[ $found ] );
$mce_init['plugins'] = implode( ',', $plugins );
return $mce_init;
}
/**
* Print shortcode plugin inline.
*
* @param array|null $mce_settings TinyMCE settings array.
*
* @return void
*/
public function print_shortcode_plugin( $mce_settings = [] ): void {
static $printed = null;
if ( null !== $printed ) {
return;
}
$printed = true;
// The `tinymce` argument of the `wp_editor()` function is set to `false`.
if ( empty( $mce_settings ) && ! ( doing_action( 'print_default_editor_scripts' ) && user_can_richedit() ) ) {
return;
}
if ( empty( $this->editors ) ) {
return;
}
?>
<script type="text/javascript">
<?php echo $this->get_l10n(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo file_get_contents( ADVADS_ABSPATH . 'assets/js/admin/shortcode.js' ); // phpcs:ignore ?>
</script>
<?php
}
/**
* Prints html select field for shortcode creator
*
* @return void
*/
public function get_content_for_shortcode_creator(): void {
if ( ! ( current_user_can( 'edit_posts' ) || current_user_can( 'edit_pages' ) ) ) {
return;
}
Data::items_dropdown( [ 'id' => 'advads-select-for-shortcode' ] );
exit();
}
/**
* Check if needed actions have not been removed by a plugin.
*
* @return array
*/
private function hooks_exist() {
if (
has_action( 'wp_tiny_mce_init', [ $this, 'print_shortcode_plugin' ] ) ||
has_action( 'print_default_editor_scripts', [ $this, 'print_shortcode_plugin' ] )
) {
return true;
}
return false;
}
/**
* Get localization strings.
*
* @return string
*/
private function get_l10n() {
$strings = [
'title' => esc_html_x( 'Add an ad', 'shortcode creator', 'advanced-ads' ),
'ok' => esc_html_x( 'Add shortcode', 'shortcode creator', 'advanced-ads' ),
'cancel' => esc_html_x( 'Cancel', 'shortcode creator', 'advanced-ads' ),
'image' => ADVADS_BASE_URL . 'assets/img/icons/tinymce-icon.png',
];
$locale = _WP_Editors::get_mce_locale();
return 'tinyMCE.addI18n("' . $locale . '.advads_shortcode", ' . wp_json_encode( $strings ) . ");\n";
}
}

View File

@@ -0,0 +1,291 @@
<?php
/**
* Admin System Info.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Framework\Utilities\Params;
defined( 'ABSPATH' ) || exit;
/**
* Admin System Info.
*/
class System_Info {
/**
* Get system information.
*
* @return string
*/
public function get_info() {
$data = '### Begin System Info ###' . "\n\n";
$data .= $this->advanced_ads_info();
$data .= $this->site_info();
$data .= $this->wp_info();
$data .= $this->uploads_info();
$data .= $this->plugins_info();
$data .= $this->server_info();
$data .= "\n" . '### End System Info ###';
return $data;
}
/**
* Get Advanced Ads info.
*
* @return string
*/
private function advanced_ads_info() {
$data = '-- Advanced Ads Info' . "\n\n";
$data .= $this->get_it_spaced( 'Pro', defined( 'AAP_VERSION' ) ? 'Activated' : 'Not Activated', 22 );
return $data;
}
/**
* Get Site info.
*
* @return string
*/
private function site_info() {
$data = "\n" . '-- Site Info' . "\n\n";
$data .= $this->get_it_spaced( 'Site URL', site_url(), 17 );
$data .= $this->get_it_spaced( 'Home URL', home_url(), 17 );
$data .= $this->get_it_spaced( 'Multisite', is_multisite() ? 'Yes' : 'No', 16 );
return $data;
}
/**
* Get WordPress Configuration info.
*
* @return string
*/
private function wp_info() {
global $wpdb;
$theme_data = wp_get_theme();
$theme = $theme_data->name . ' ' . $theme_data->version;
$data = "\n" . '-- WordPress Configuration' . "\n\n";
$data .= $this->get_it_spaced( 'Version', get_bloginfo( 'version' ), 18 );
$data .= $this->get_it_spaced( 'Language', get_locale(), 17 );
$data .= $this->get_it_spaced( 'User Language', get_user_locale(), 12 );
$data .= $this->get_it_spaced( 'Permalink Structure', get_option( 'permalink_structure' ) ?? 'Default', 6 );
$data .= $this->get_it_spaced( 'Active Theme', $theme, 13 );
$data .= $this->get_it_spaced( 'Show On Front', get_option( 'show_on_front' ), 12 );
$data .= $this->get_it_spaced( 'ABSPATH', ABSPATH, 18 );
$data .= $this->get_it_spaced( 'Table Prefix', 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable' ), 13 ); //phpcs:ignore
$data .= $this->get_it_spaced( 'WP_DEBUG', defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set', 17 );
$data .= $this->get_it_spaced( 'Memory Limit', WP_MEMORY_LIMIT, 13 );
$data .= $this->get_it_spaced( 'Registered Post Stati', implode( ', ', get_post_stati() ), 4 );
$data .= $this->get_it_spaced( 'Revisions', WP_POST_REVISIONS ? WP_POST_REVISIONS > 1 ? 'Limited to ' . WP_POST_REVISIONS : 'Enabled' : 'Disabled', 16 );
return $data;
}
/**
* Get Uploads/Constants info.
*
* @return string
*/
private function uploads_info() {
$uploads_dir = wp_upload_dir();
$data = "\n" . '-- WordPress Uploads/Constants' . "\n\n";
$data .= $this->get_it_spaced( 'WP_CONTENT_DIR', defined( 'WP_CONTENT_DIR' ) ? WP_CONTENT_DIR ? WP_CONTENT_DIR : 'Disabled' : 'Not set', 11 );
$data .= $this->get_it_spaced( 'WP_CONTENT_URL', defined( 'WP_CONTENT_URL' ) ? WP_CONTENT_URL ? WP_CONTENT_URL : 'Disabled' : 'Not set', 11 );
$data .= $this->get_it_spaced( 'UPLOADS', defined( 'UPLOADS' ) ? UPLOADS ? UPLOADS : 'Disabled' : 'Not set', 18 );
$data .= $this->get_it_spaced( 'wp_uploads_dir() path', $uploads_dir['path'], 4 );
$data .= $this->get_it_spaced( 'wp_uploads_dir() url', $uploads_dir['url'], 5 );
$data .= $this->get_it_spaced( 'wp_uploads_dir() basedir', $uploads_dir['basedir'], 1 );
$data .= $this->get_it_spaced( 'wp_uploads_dir() baseurl', $uploads_dir['baseurl'], 1 );
return $data;
}
/**
* Get Plugins info.
*
* @return string
*/
private function plugins_info() {
$data = $this->mu_plugins();
$data .= $this->installed_plugins();
$data .= $this->multisite_plugins();
return $data;
}
/**
* Get MU Plugins info.
*
* @return string
*/
private function mu_plugins() {
$data = '';
// Must-use plugins.
// NOTE: MU plugins can't show updates!
$muplugins = get_mu_plugins();
if ( ! empty( $muplugins ) && count( $muplugins ) > 0 ) {
$data = "\n" . '-- Must-Use Plugins' . "\n\n";
foreach ( $muplugins as $plugin => $plugin_data ) {
$data .= $plugin_data['Name'] . ': ' . $plugin_data['Version'] . "\n";
}
}
return $data;
}
/**
* Get Installed Plugins info.
*
* @return string
*/
private function installed_plugins() {
$updates = get_plugin_updates();
// WordPress active plugins.
$data = "\n" . '-- WordPress Active Plugins' . "\n\n";
$plugins = get_plugins();
$active_plugins = get_option( 'active_plugins', [] );
foreach ( $plugins as $plugin_path => $plugin ) {
if ( ! in_array( $plugin_path, $active_plugins, true ) ) {
continue;
}
$update = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
$data .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
}
// WordPress inactive plugins.
$data .= "\n" . '-- WordPress Inactive Plugins' . "\n\n";
foreach ( $plugins as $plugin_path => $plugin ) {
if ( in_array( $plugin_path, $active_plugins, true ) ) {
continue;
}
$update = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
$data .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
}
return $data;
}
/**
* Get Multisite Plugins info.
*
* @return string
*/
private function multisite_plugins() {
$data = '';
if ( ! is_multisite() ) {
return $data;
}
$updates = get_plugin_updates();
// WordPress Multisite active plugins.
$data = "\n" . '-- Network Active Plugins' . "\n\n";
$plugins = wp_get_active_network_plugins();
$active_plugins = get_site_option( 'active_sitewide_plugins', [] );
foreach ( $plugins as $plugin_path ) {
$plugin_base = plugin_basename( $plugin_path );
if ( ! array_key_exists( $plugin_base, $active_plugins ) ) {
continue;
}
$update = ( array_key_exists( $plugin_path, $updates ) ) ? ' (needs update - ' . $updates[ $plugin_path ]->update->new_version . ')' : '';
$plugin = get_plugin_data( $plugin_path );
$data .= $plugin['Name'] . ': ' . $plugin['Version'] . $update . "\n";
}
return $data;
}
/**
* Get Server info.
*
* @return string
*/
private function server_info() {
global $wpdb;
// Server configuration (really just versions).
$software = Params::server( 'SERVER_SOFTWARE', '' );
$software = sanitize_text_field( wp_unslash( $software ) );
$data = "\n" . '-- Webserver Configuration' . "\n\n";
$data .= $this->get_it_spaced( 'PHP Version:', PHP_VERSION, 14 );
$data .= $this->get_it_spaced( 'MySQL Version', $wpdb->db_version(), 13 );
$data .= $this->get_it_spaced( 'Webserver Info', $software, 12 );
// PHP configs... now we're getting to the important stuff.
$data .= "\n" . '-- PHP Configuration' . "\n\n";
$data .= $this->get_it_spaced( 'Memory Limit', ini_get( 'memory_limit' ), 13 );
$data .= $this->get_it_spaced( 'Upload Max Size', ini_get( 'upload_max_filesize' ), 10 );
$data .= $this->get_it_spaced( 'Post Max Size', ini_get( 'post_max_size' ), 12 );
$data .= $this->get_it_spaced( 'Upload Max Filesize', ini_get( 'upload_max_filesize' ), 6 );
$data .= $this->get_it_spaced( 'Time Limit', ini_get( 'max_execution_time' ), 15 );
$data .= $this->get_it_spaced( 'Max Input Vars', ini_get( 'max_input_vars' ), 11 );
$data .= $this->get_it_spaced( 'Display Errors', ( ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A' ), 11 );
// PHP extensions and such.
$data .= "\n" . '-- PHP Extensions' . "\n\n";
$data .= $this->get_it_spaced( 'cURL', function_exists( 'curl_init' ) ? 'Supported' : 'Not Supported', 21 );
$data .= $this->get_it_spaced( 'fsockopen', function_exists( 'fsockopen' ) ? 'Supported' : 'Not Supported', 16 );
$data .= $this->get_it_spaced( 'SOAP Client', class_exists( 'SoapClient', false ) ? 'Installed' : 'Not Installed', 14 );
$data .= $this->get_it_spaced( 'Suhosin', extension_loaded( 'suhosin' ) ? 'Installed' : 'Not Installed', 18 );
// Session stuff.
$data .= "\n" . '-- Session Configuration' . "\n\n";
$data .= $this->get_it_spaced( 'Session', isset( $_SESSION ) ? 'Enabled' : 'Disabled', 18 );
// The rest of this is only relevant if session is enabled.
if ( isset( $_SESSION ) ) {
$data .= $this->get_it_spaced( 'Session Name', esc_html( ini_get( 'session.name' ) ), 13 );
$data .= $this->get_it_spaced( 'Cookie Path', esc_html( ini_get( 'session.cookie_path' ) ), 14 );
$data .= $this->get_it_spaced( 'Save Path', esc_html( ini_get( 'session.save_path' ) ), 16 );
$data .= $this->get_it_spaced( 'Use Cookies', ( ini_get( 'session.use_cookies' ) ? 'On' : 'Off' ), 14 );
$data .= $this->get_it_spaced( 'Use Only Cookies', ( ini_get( 'session.use_only_cookies' ) ? 'On' : 'Off' ), 9 );
}
return $data;
}
/**
* Consistent spacing in labels
*
* @param string $label Label of data.
* @param string $value Value of data.
* @param int $space Space count.
*
* @return string
*/
private function get_it_spaced( $label, $value, $space = 9 ): string {
return sprintf(
'%1$s:%2$s%3$s' . "\n",
$label,
str_repeat( ' ', $space ),
$value
);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* The class is responsible for configuring the TinyMCE editor to allow unsafe link targets.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* TinyMCE.
*/
class TinyMCE implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'tiny_mce_before_init', [ $this, 'allow_unsafe_link_target' ] );
}
/**
* Configure TinyMCE to allow unsafe link target.
*
* @param bool $mce_init the tinyMce initialization array.
*
* @return bool|[]
*/
public function allow_unsafe_link_target( $mce_init ) {
// Early bail!!
if ( ! function_exists( 'get_current_screen' ) ) {
return $mce_init;
}
$screen = get_current_screen();
if ( 'advanced_ads' === ( $screen->id ?? '' ) ) {
$mce_init['allow_unsafe_link_target'] = true;
}
return $mce_init;
}
}

View File

@@ -0,0 +1,456 @@
<?php
/**
* Translation Promo.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.45.0
*/
namespace AdvancedAds\Admin;
defined( 'ABSPATH' ) || exit;
/**
* This class defines a promo box and checks your translation site's API for stats about it.
*
* @copyright Yoast i18n https://github.com/Yoast/i18n-module
*
* phpcs:disable WordPress.WP.I18n.NonSingularStringLiteralDomain
*/
class Translation_Promo {
/**
* Your translation site's logo.
*
* @var string
*/
private $glotpress_logo;
/**
* Your translation site's name.
*
* @var string
*/
private $glotpress_name;
/**
* Your translation site's URL.
*
* @var string
*/
private $glotpress_url;
/**
* The URL to actually do the API request to.
*
* @var string
*/
private $api_url;
/**
* Hook where you want to show the promo box.
*
* @var string
*/
private $hook;
/**
* Will contain the site's locale.
*
* @access private
* @var string
*/
private $locale;
/**
* Will contain the locale's name, obtained from your translation site.
*
* @access private
* @var string
*/
private $locale_name;
/**
* Will contain the percentage translated for the plugin translation project in the locale.
*
* @access private
* @var int
*/
private $percent_translated;
/**
* Name of your plugin.
*
* @var string
*/
private $plugin_name;
/**
* Project slug for the project on your translation site.
*
* @var string
*/
private $project_slug;
/**
* URL to point to for registration links.
*
* @var string
*/
private $register_url;
/**
* Your plugins textdomain.
*
* @var string
*/
private $textdomain;
/**
* Indicates whether there's a translation available at all.
*
* @access private
* @var bool
*/
private $translation_exists;
/**
* Indicates whether the translation's loaded.
*
* @access private
* @var bool
*/
private $translation_loaded;
/**
* Constructs the i18n module for wordpress.org.
*
* Required fields are the 'textdomain', 'plugin_name' and 'hook'.
*
* @param array $args The settings for the i18n module.
* @param bool $show_translation_box Whether the translation box should be shown.
*/
public function __construct( $args, $show_translation_box = true ) {
if ( ! is_admin() ) {
return;
}
$args = $this->set_defaults( $args );
$this->locale = $this->get_admin_locale();
if ( $this->is_default_language( $this->locale ) ) {
return;
}
$this->init( $args );
if ( $show_translation_box ) {
add_action( $this->hook, [ $this, 'promo' ] );
}
$this->set_api_url( $args['textdomain'] );
}
/**
* Returns whether the language is en_US.
*
* @param string $language The language to check.
*
* @return bool Returns true if the language is en_US.
*/
protected function is_default_language( $language ) {
return 'en_US' === $language;
}
/**
* Returns the locale used in the admin.
*
* WordPress 4.7 introduced the ability for users to specify an Admin language
* different from the language used on the front end. This checks if the feature
* is available and returns the user's language, with a fallback to the site's language.
* Can be removed when support for WordPress 4.6 will be dropped, in favor
* of WordPress get_user_locale() that already fallbacks to the sites locale.
*
* @returns string The locale.
*/
private function get_admin_locale() {
if ( function_exists( 'get_user_locale' ) ) {
return get_user_locale();
}
return get_locale();
}
/**
* This is where you decide where to display the messages and where you set the plugin specific variables.
*
* @access private
*
* @param array $args Contains the settings for the class.
*/
private function init( $args ) {
foreach ( $args as $key => $arg ) {
$this->$key = $arg;
}
}
/**
* Check whether the promo should be hidden or not.
*
* @access private
*
* @return bool
*/
private function hide_promo() {
$hide_promo = get_transient( 'yoast_i18n_' . $this->project_slug . '_promo_hide' );
if ( ! $hide_promo ) {
if ( filter_input( INPUT_GET, 'remove_i18n_promo', FILTER_VALIDATE_INT ) === 1 ) {
// No expiration time, so this would normally not expire, but it wouldn't be copied to other sites etc.
set_transient( 'yoast_i18n_' . $this->project_slug . '_promo_hide', true );
$hide_promo = true;
}
}
return $hide_promo;
}
/**
* Returns the i18n_promo message from the i18n_module. Returns en empty string if the promo shouldn't be shown.
*
* @access public
*
* @return string The i18n promo message.
*/
public function get_promo_message() {
if ( ! $this->is_default_language( $this->locale ) && ! $this->hide_promo() ) {
return $this->promo_message();
}
return '';
}
/**
* Generates a promo message.
*
* @access private
*
* @return bool|string $message
*/
private function promo_message() {
$this->translation_details();
$message = false;
if ( $this->translation_exists && $this->translation_loaded && $this->percent_translated < 90 ) {
/* translators: 1: language name; 3: completion percentage; 4: link to translation platform. */
$message = __( 'As you can see, there is a translation of this plugin in %1$s. This translation is currently %3$d%% complete. We need your help to make it complete and to fix any errors. Please register at %4$s to help complete the translation to %1$s!', $this->textdomain );
} elseif ( ! $this->translation_loaded && $this->translation_exists ) {
/* translators: 1: language name; 2: plugin name; 3: completion percentage; 4: link to translation platform. */
$message = __( 'You\'re using WordPress in %1$s. While %2$s has been translated to %1$s for %3$d%%, it\'s not been shipped with the plugin yet. You can help! Register at %4$s to help complete the translation to %1$s!', $this->textdomain );
} elseif ( ! $this->translation_exists ) {
/* translators: 2: plugin name; 4: link to translation platform. */
$message = __( 'You\'re using WordPress in a language we don\'t support yet. We\'d love for %2$s to be translated in that language too, but unfortunately, it isn\'t right now. You can change that! Register at %4$s to help translate it!', $this->textdomain );
}
$registration_link = sprintf(
'<a href="%1$s">%2$s</a>',
esc_url( $this->register_url ),
esc_html( $this->glotpress_name )
);
$message = sprintf(
esc_html( $message ),
esc_html( $this->locale_name ),
esc_html( $this->plugin_name ),
(int) $this->percent_translated,
$registration_link
);
if ( $message ) {
$message = '<p>' . $message . '</p><p><a href="' . esc_url( $this->register_url ) . '">' . esc_html__( 'Register now &raquo;', $this->textdomain ) . '</a></p>';
}
return $message;
}
/**
* Returns a button that can be used to dismiss the i18n-message.
*
* @access private
*
* @return string
*/
public function get_dismiss_i18n_message_button() {
return sprintf(
/* translators: %1$s is the notification dismissal link start tag, %2$s is the link closing tag. */
esc_html__( '%1$sPlease don\'t show me this notification anymore%2$s', $this->textdomain ),
'<a class="button" href="' . esc_url( add_query_arg( [ 'remove_i18n_promo' => '1' ] ) ) . '">',
'</a>'
);
}
/**
* Sets the default values for wordpress.org
*
* @param array $args The arguments to set defaults for.
*
* @return array The arguments with the arguments set.
*/
private function set_defaults( $args ) {
if ( ! isset( $args['glotpress_logo'] ) ) {
$args['glotpress_logo'] = 'https://plugins.svn.wordpress.org/' . $args['textdomain'] . '/assets/icon-128x128.png';
}
if ( ! isset( $args['register_url'] ) ) {
$args['register_url'] = 'https://translate.wordpress.org/projects/wp-plugins/' . $args['textdomain'] . '/';
}
if ( ! isset( $args['glotpress_name'] ) ) {
$args['glotpress_name'] = 'Translating WordPress';
}
if ( ! isset( $args['project_slug'] ) ) {
$args['project_slug'] = $args['textdomain'];
}
return $args;
}
/**
* Outputs a promo box.
*
* @access public
*/
public function promo() {
$message = $this->get_promo_message();
if ( $message ) {
echo '<div id="i18n_promo_box" style="border:1px solid #ccc;background-color:#fff;padding:10px;max-width:650px; overflow: hidden;">';
echo '<a href="' . esc_url( add_query_arg( [ 'remove_i18n_promo' => '1' ] ) ) . '" style="color:#333;text-decoration:none;font-weight:bold;font-size:16px;border:1px solid #ccc;padding:1px 4px;" class="alignright">X</a>';
echo '<div>';
/* translators: %s: plugin name. */
echo '<h2>' . sprintf( esc_html__( 'Translation of %s', $this->textdomain ), esc_html( $this->plugin_name ) ) . '</h2>';
if ( isset( $this->glotpress_logo ) && is_string( $this->glotpress_logo ) && '' !== $this->glotpress_logo ) {
echo '<a href="' . esc_url( $this->register_url ) . '"><img class="alignright" style="margin:0 5px 5px 5px;max-width:200px;" src="' . esc_url( $this->glotpress_logo ) . '" alt="' . esc_attr( $this->glotpress_name ) . '"/></a>';
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- correctly escaped in promo_message() method.
echo $message;
echo '</div>';
echo '</div>';
}
}
/**
* Try to find the transient for the translation set or retrieve them.
*
* @access private
*
* @return object|null
*/
private function find_or_initialize_translation_details() {
$set = get_transient( 'yoast_i18n_' . $this->project_slug . '_' . $this->locale );
if ( ! $set ) {
$set = $this->retrieve_translation_details();
set_transient( 'yoast_i18n_' . $this->project_slug . '_' . $this->locale, $set, DAY_IN_SECONDS );
}
return $set;
}
/**
* Try to get translation details from cache, otherwise retrieve them, then parse them.
*
* @access private
*/
private function translation_details() {
$set = $this->find_or_initialize_translation_details();
$this->translation_exists = ! is_null( $set );
$this->translation_loaded = is_textdomain_loaded( $this->textdomain );
$this->parse_translation_set( $set );
}
/**
* Set the API URL on the i18n object.
*
* @param string $textdomain The textdomain to use for the API URL.
*/
private function set_api_url( $textdomain ) {
$this->api_url = 'https://translate.wordpress.org/api/projects/wp-plugins/' . $textdomain . '/stable/';
}
/**
* Returns the API URL to use when requesting translation information.
*
* @return string
*/
private function get_api_url() {
if ( empty( $this->api_url ) ) {
$this->api_url = trailingslashit( $this->glotpress_url ) . 'api/projects/' . $this->project_slug;
}
return $this->api_url;
}
/**
* Retrieve the translation details from Yoast Translate.
*
* @access private
*
* @return object|null
*/
private function retrieve_translation_details() {
$api_url = $this->get_api_url();
$resp = wp_remote_get( $api_url );
if ( is_wp_error( $resp ) || wp_remote_retrieve_response_code( $resp ) !== 200 ) {
return null;
}
$body = wp_remote_retrieve_body( $resp );
unset( $resp );
if ( $body ) {
$body = json_decode( $body );
if ( empty( $body->translation_sets ) ) {
return null;
}
foreach ( $body->translation_sets as $set ) {
if ( ! property_exists( $set, 'wp_locale' ) ) {
continue;
}
// For informal and formal locales, we have to complete the locale code by concatenating the slug ('formal' or 'informal') to the xx_XX part.
if ( 'default' !== $set->slug && strtolower( $this->locale ) === strtolower( $set->wp_locale . '_' . $set->slug ) ) {
return $set;
}
if ( $this->locale === $set->wp_locale ) {
return $set;
}
}
}
return null;
}
/**
* Set the needed private variables based on the results from Yoast Translate.
*
* @param object $set The translation set.
*
* @access private
*/
private function parse_translation_set( $set ) {
$this->locale_name = '';
$this->percent_translated = '';
if ( $this->translation_exists && is_object( $set ) ) {
$this->locale_name = $set->name;
$this->percent_translated = $set->percent_translated;
}
}
}

View File

@@ -0,0 +1,202 @@
<?php
/**
* The class is responsible for holding promoting upgrade related functionality.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.0.0
*/
namespace AdvancedAds\Admin;
defined( 'ABSPATH' ) || exit;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
/**
* Class upgrades
*/
class Upgrades implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
// Show notice in Ad Parameters when someone uses an Ad Manager ad in the plain text code field.
add_filter( 'advanced-ads-ad-notices', [ $this, 'ad_notices' ], 10, 3 );
// Show AMP options on ad edit page of AdSense ads.
add_action( 'advanced-ads-gadsense-extra-ad-param', [ $this, 'adsense_type_amp_options' ] );
// Add Duplicate.
add_filter( 'post_row_actions', [ $this, 'render_duplicate_link' ], 10, 2 );
add_filter( 'post_row_actions', [ $this, 'render_placement_duplicate_link' ], 10, 2 );
add_action( 'post_submitbox_start', [ $this, 'render_duplicate_link_in_submit_box' ] );
}
/**
* Show an upgrade link
*
* @param string $title link text.
* @param string $url target URL.
* @param string $utm_campaign utm_campaign value to attach to the URL.
*
* @return void
*/
public static function upgrade_link( $title = '', $url = '', $utm_campaign = 'upgrade' ): void {
$title = ! empty( $title ) ? $title : __( 'Upgrade', 'advanced-ads' );
$url = ! empty( $url ) ? $url : 'https://wpadvancedads.com/add-ons/';
$url = add_query_arg(
[
'utm_source' => 'advanced-ads',
'utm_medium' => 'link',
'utm_campaign' => $utm_campaign,
],
$url
);
include ADVADS_ABSPATH . 'admin/views/upgrades/upgrade-link.php';
}
/**
* Show an Advanced Ads Pro upsell pitch
*
* @param string $utm_campaign utm_campaign value to attach to the URL.
*
* @return void
*/
public static function pro_feature_link( $utm_campaign = '' ): void {
self::upgrade_link(
__( 'Pro Feature', 'advanced-ads' ),
'https://wpadvancedads.com/advanced-ads-pro/',
$utm_campaign
);
}
/**
* Show notices in the Ad Parameters meta box
*
* @param array $notices Notices.
* @param array $box current meta box.
* @param Ad $ad post object.
*
* @return array
*/
public function ad_notices( $notices, $box, $ad ) {
// Show notice when someone uses an Ad Manager ad in the plain text code field.
if ( ! defined( 'AAGAM_VERSION' ) && 'ad-parameters-box' === $box['id'] ) {
if ( $ad->is_type( 'plain' ) && strpos( $ad->get_content(), 'div-gpt-ad-' ) ) {
$notices[] = [
'text' => sprintf(
/* translators: %1$s and %2$s are opening and closing <a> tags */
esc_html__( 'This looks like a Google Ad Manager ad. Use the %1$sGAM Integration%2$s.', 'advanced-ads' ),
'<a href="https://wpadvancedads.com/add-ons/google-ad-manager/?utm_source=advanced-ads&utm_medium=link&utm_campaign=upgrade-ad-parameters-gam" target="_blank">',
'</a>'
) . ' ' . __( 'A quick and error-free way of implementing ad units from your Google Ad Manager account.', 'advanced-ads' ),
];
}
}
return $notices;
}
/**
* AMP options for AdSense ads in the Ad Parameters on the ad edit page.
*/
public function adsense_type_amp_options() {
if ( ! defined( 'AAR_VERSION' ) && \Advanced_Ads_Checks::active_amp_plugin() ) {
include_once ADVADS_ABSPATH . 'admin/views/upgrades/adsense-amp.php';
}
}
/**
* Add the link to action list for post_row_actions
*
* @param array $actions list of existing actions.
* @param WP_Post $post Post object.
*
* @return array with actions.
*/
public function render_duplicate_link( $actions, $post ) {
if (
! defined( 'AAP_VERSION' )
&& Constants::POST_TYPE_AD === $post->post_type
&& Conditional::user_can( 'advanced_ads_edit_ads' )
) {
$actions['copy-ad'] = $this->create_duplicate_link();
}
return $actions;
}
/**
* Add the link to action list for placements.
*
* @param array $actions list of existing actions.
* @param WP_Post $post Post object.
*
* @return array with actions.
*/
public function render_placement_duplicate_link( $actions, $post ) {
if (
! defined( 'AAP_VERSION' )
&& Constants::POST_TYPE_PLACEMENT === $post->post_type
&& Conditional::user_can( 'advanced_ads_edit_ads' )
) {
$actions['copy-ad'] = $this->create_duplicate_link( Constants::POST_TYPE_PLACEMENT );
}
return $actions;
}
/**
* Add the link to the submit box on the ad edit screen.
*/
public function render_duplicate_link_in_submit_box() {
global $post;
if (
! defined( 'AAP_VERSION' )
&& 'edit' === $post->filter // only for already saved ads.
&& Constants::POST_TYPE_AD === $post->post_type
&& Conditional::user_can( 'advanced_ads_edit_ads' )
) {
?>
<div>
<?php echo wp_kses_post( $this->create_duplicate_link() ); ?>
</div>
<?php
}
}
/**
* Generate text and upgrade link for the Duplicate function
*
* @param string $post_type post type.
*/
public function create_duplicate_link( $post_type = Constants::POST_TYPE_AD ) {
ob_start();
$utm_campaign = ( Constants::POST_TYPE_PLACEMENT === $post_type ) ? 'duplicate-placement' : 'duplicate-ad';
self::upgrade_link(
null,
sprintf(
'https://wpadvancedads.com/advanced-ads-pro/?utm_source=advanced-ads&utm_medium=link&utm_campaign=%s',
$utm_campaign
),
$utm_campaign
);
return sprintf(
'%1$s (%2$s)',
esc_html__( 'Duplicate', 'advanced-ads' ),
trim( ob_get_clean() )
);
}
}

View File

@@ -0,0 +1,222 @@
<?php
/**
* Admin Version Control.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Version Control.
*/
class Version_Control implements Integration_Interface {
/**
* Includes up to this amount of latest minor version into the usable version, including all the in between patches.
*
* @var int
*/
private const MINOR_VERSION_COUNT = 3;
/**
* The version list transient name
*
* @var string
*/
public const VERSIONS_TRANSIENT = 'advads-versions-list';
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'wp_ajax_advads_get_usable_versions', [ $this, 'get_usable_versions' ] );
add_action( 'wp_ajax_advads_install_alternate_version', [ $this, 'install_plugin' ] );
}
/**
* Download and install the desired version
*
* @return void
*/
public function install_plugin(): void {
$this->check_user_capabilities();
wp_parse_str( Params::post( 'vars', '' ), $args );
$nonce = sanitize_key( $args['nonce'] ) ?? '';
if ( ! wp_verify_nonce( $nonce, 'advads-version-control' ) ) {
wp_send_json_error( 'Not authorized', 401 );
}
$exploded = explode( '|', $args['version'] );
$version = sanitize_text_field( $exploded[0] );
$package = sanitize_url( $exploded[1] );
$installer = new Plugin_Installer( $version, $package );
$result = $installer->install();
if ( is_wp_error( $result ) ) {
wp_send_json_error(
[
'error_code' => $result->get_error_code(),
'error_message' => $result->get_error_message(),
],
400
);
}
if ( version_compare( $version, '2.0.0', '<' ) ) {
$placements = get_option( 'advads-ads-placements_backup', true );
update_option( 'advads-ads-placements', $placements );
delete_option( 'advads-ads-placements_backup' );
update_option( 'advanced_ads_db_version', '1.52.1' );
}
activate_plugin( plugin_basename( ADVADS_ABSPATH . basename( ADVADS_FILE ) ) );
wp_send_json_success(
[
'result' => $result,
'redirect' => admin_url( 'plugins.php?rollback=1' ),
],
200
);
}
/**
* Get usable version, fetch from the info API if needed
*
* @return mixed|void
*/
public function get_usable_versions() {
$this->check_user_capabilities();
if ( ! wp_verify_nonce( Params::post( 'nonce', '', FILTER_SANITIZE_FULL_SPECIAL_CHARS ), 'advads-version-control' ) ) {
wp_send_json_error( 'Not authorized', 401 );
}
$stored_versions = get_transient( self::VERSIONS_TRANSIENT );
if ( $stored_versions ) {
if ( wp_doing_ajax() ) {
wp_send_json_success( $stored_versions, 200 );
}
return $stored_versions;
}
$versions = $this->get_version_from_api();
if ( is_wp_error( $versions ) ) {
wp_send_json_error( $versions->get_error_message() . '>>' . $versions->get_error_message(), $versions->get_error_code() );
}
$versions = $this->filter_version_number( $versions );
set_transient( self::VERSIONS_TRANSIENT, $versions, 3 * HOUR_IN_SECONDS );
wp_send_json_success( $versions, 200 );
}
/**
* Perform capabilities check
*
* @return void
*/
private function check_user_capabilities() {
if ( ! current_user_can( 'install_plugins' ) ) {
wp_send_json_error( 'Not enough permissions', 401 );
}
}
/**
* Filter the versions list from the info API
*
* - all updates until the last three minor updates
* - the last version before the last major update
*
* @param array $versions all version the info API.
*
* @return array
*/
public function filter_version_number( $versions ) {
$results = [];
// Remove the "dev" version.
unset( $versions['trunk'] );
$version_numbers = array_keys( $versions );
usort( $version_numbers, 'version_compare' );
$version_numbers = array_reverse( $version_numbers );
array_shift( $version_numbers );
$major = '';
$minor = '';
$minor_version_changes = 0;
$major_version_changes = 0;
foreach ( $version_numbers as $number ) {
// Skip pre-release versions.
if ( preg_match( '/(rc|alpha|beta)/i', $number ) ) {
continue;
}
$parts = explode( '.', $number );
$major_part = $parts[1];
$minor_part = $parts[2];
if ( $major !== $major_part ) {
$major = $major_part;
++$major_version_changes;
$minor_version_changes = 0;
}
if ( $minor !== $minor_part ) {
$minor = $minor_part;
++$minor_version_changes;
}
if ( $minor_version_changes <= self::MINOR_VERSION_COUNT ) {
$results[ $number ] = $versions[ $number ];
}
if ( $major_version_changes >= self::MINOR_VERSION_COUNT ) {
break;
}
}
return [
'versions' => $results,
'order' => array_keys( $results ),
];
}
/**
* Get all version from the info API
*
* @return array|\WP_Error
*/
private function get_version_from_api() {
$aa_info = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/advanced-ads.json' );
if ( is_wp_error( $aa_info ) ) {
return $aa_info;
}
$info = json_decode( wp_remote_retrieve_body( $aa_info ), true );
if ( $info['versions'] ) {
return $info['versions'];
}
// Likely a change in the WP info API.
return new \WP_Error( 404, __( 'Plugin info not found', 'advanced-ads' ) );
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* Admin Welcome.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Admin Welcome.
*/
class Welcome {
/**
* Dismiss user meta
*
* @var string
*/
const USER_META = 'advanced-ads-welcome';
/**
* Main instance
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Welcome
*/
public static function get() {
static $instance;
if ( null === $instance ) {
$instance = new Welcome();
}
return $instance;
}
/**
* Print the welcome box
*
* @return void
*/
public function display() {
if ( ! $this->can_display() ) {
return;
}
require_once plugin_dir_path( ADVADS_FILE ) . '/views/admin/welcome-box.php';
}
/**
* Stop displaying the welcome box fot the current user
*
* @return void
*/
public function dismiss() {
update_user_meta( get_current_user_id(), self::USER_META, '1' );
}
/**
* Checks if the welcome box can be displayed
*
* @return bool
*/
public function can_display(): bool {
$meta = get_user_meta( get_current_user_id(), self::USER_META, true );
$wizard_done = get_option( Constants::OPTION_WIZARD_COMPLETED, false );
return empty( $meta ) && ! $wizard_done;
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* Admin WordPress Dashboard.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin;
use AdvancedAds\Options;
use Advanced_Ads_AdSense_Data;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
use Advanced_Ads_Overview_Widgets_Callbacks;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* WordPress Dashboard.
*/
class WordPress_Dashboard implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'wp_dashboard_setup', [ $this, 'add_adsense_widget' ] );
add_action( 'wp_dashboard_setup', [ $this, 'add_dashboard_widget' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ], 10, 0 );
add_action( 'advanced-ads-dashbaord-widget', [ $this, 'display_performing_ads' ] );
add_action( 'advanced-ads-dashbaord-widget', [ $this, 'display_rss_widget' ] );
}
/**
* Enqueue styles and scripts for current screen
*
* @return void
*/
public function enqueue(): void {
// Early bail!!
$wp_screen = get_current_screen();
if ( 'dashboard' !== $wp_screen->id ) {
return;
}
wp_advads()->registry->enqueue_style( 'wp-dashboard' );
wp_advads()->registry->enqueue_script( 'wp-dashboard' );
}
/**
* Add dashboard widget with ad stats and additional information
*
* @return void
*/
public function add_dashboard_widget(): void {
if ( ! Conditional::user_can( 'advanced_ads_see_interface' ) ) {
return;
}
$icon = WordPress::get_svg( 'logo.svg' );
$icon = '<span class="advads-logo--icon">' . $icon . '</span>';
wp_add_dashboard_widget(
'advads-dashboard-widget',
$icon . '<span class="advads-logo--text">' . __( 'Advanced Ads', 'advanced-ads' ) . '</span>',
[ $this, 'display_dashboard_widget' ],
null,
null,
'side',
'high'
);
}
/**
* Adds an AdSense widget to the WordPress dashboard.
*
* @return void
*/
public function add_adsense_widget(): void {
if (
Advanced_Ads_AdSense_Data::get_instance()->is_setup() &&
! Advanced_Ads_AdSense_Data::get_instance()->is_hide_stats() &&
Options::instance()->get( 'adsense.adsense-wp-widget' )
) {
wp_add_dashboard_widget(
'advads-adsense-widget',
__( 'AdSense Earnings', 'advanced-ads' ),
[ $this, 'display_adsense_widget' ],
null,
null,
'side'
);
}
}
/**
* Display widget functions
*
* @return void
*/
public function display_dashboard_widget(): void {
if ( Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
include ADVADS_ABSPATH . 'views/admin/widgets/wordpress-dashboard/header.php';
}
if ( Conditional::user_can_subscribe( 'nl_first_steps' ) || Conditional::user_can_subscribe( 'nl_adsense' ) ) {
include ADVADS_ABSPATH . 'views/admin/widgets/wordpress-dashboard/newsletter.php';
}
/**
* Let developer add KPIs and info into dashabord
*
* @param WordPress_Dashboard $this Dashabord widget instance.
*/
do_action( 'advanced-ads-dashbaord-widget', $this );
include ADVADS_ABSPATH . 'views/admin/widgets/wordpress-dashboard/footer.php';
}
/**
* Display the AdSense widget on the WordPress dashboard.
*
* @return void
*/
public function display_adsense_widget(): void {
Advanced_Ads_Overview_Widgets_Callbacks::render_adsense_stats();
}
/**
* Display performing ads widget
*
* @return void
*/
public function display_performing_ads(): void {
include_once ADVADS_ABSPATH . 'views/admin/widgets/wordpress-dashboard/performing-ads.php';
}
/**
* Display rss widget
*
* @return void
*/
public function display_rss_widget(): void {
include_once ADVADS_ABSPATH . 'views/admin/widgets/wordpress-dashboard/rss.php';
}
}

View File

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

View File

@@ -0,0 +1,103 @@
<?php
/**
* Ad Adsense.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin\Metaboxes;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use Advanced_Ads_AdSense_Data;
use Advanced_Ads_Network_Adsense;
defined( 'ABSPATH' ) || exit;
/**
* Ad Adsense.
*/
class Ad_Adsense {
/**
* Get metabox id
*
* @return string
*/
public function get_box_id(): string {
return 'advads-gadsense-box';
}
/**
* Hook into WordPress.
*
* TODO: move to module to its right place.
*
* @param Metabox_Ad $manager Manager instance.
*
* @return void
*/
public function register( $manager ): void {
global $post;
if (
$post->ID &&
Advanced_Ads_AdSense_Data::get_instance()->is_setup() &&
! Advanced_Ads_AdSense_Data::get_instance()->is_hide_stats()
) {
$ad_unit = Advanced_Ads_Network_Adsense::get_instance()->get_ad_unit( $post->ID );
if ( $ad_unit ) {
add_meta_box(
$this->get_box_id(),
sprintf(
/* translators: 1: Name of ad unit */
esc_html__( 'Earnings of %1$s', 'advanced-ads' ),
esc_html( $ad_unit->name )
),
[ $manager, 'display' ],
Constants::POST_TYPE_AD,
'normal',
'high'
);
}
}
}
/**
* Get metaboxe view file
*
* @param Ad $ad Ad instance.
*
* @return string
*/
public function get_view( $ad ): string {
$unit_code = null;
if ( $ad->is_type( 'adsense' ) && ! empty( $ad->get_content() ) ) {
$json_content = json_decode( $ad->get_content() );
// phpcs:disable
if ( isset( $json_content->slotId ) ) {
$unit_code = $json_content->slotId;
}
// phpcs:enable
}
$report_type = 'unit';
$report_filter = $unit_code;
include ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-gadsense-dashboard.php';
return '';
}
/**
* Return manual link
*
* @return array|string
*/
public function get_handle_link() {
return '<a href="' . esc_url( admin_url( 'admin.php?page=advanced-ads-settings#top#adsense' ) ) . '" target="_blank">' . __( 'Disable', 'advanced-ads' ) . '</a>';
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* Ad Layout.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin\Metaboxes;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Ad Layout.
*/
class Ad_Layout {
/**
* Get metabox id
*
* @return string
*/
public function get_box_id(): string {
return 'ad-output-box';
}
/**
* Hook into WordPress.
*
* @param Metabox_Ad $manager Manager instance.
*
* @return void
*/
public function register( $manager ): void {
add_meta_box(
$this->get_box_id(),
__( 'Layout / Output', 'advanced-ads' ),
[ $manager, 'display' ],
Constants::POST_TYPE_AD,
'normal',
'high'
);
}
/**
* Get metaboxe view file
*
* @return string
*/
public function get_view(): string {
return ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-layout.php';
}
/**
* Return manual link
*
* @return array|string
*/
public function get_handle_link() {
return '<a href="https://wpadvancedads.com/manual/optimizing-the-ad-layout/?utm_source=advanced-ads&utm_medium=link&utm_campaign=edit-ad-layout" target="_blank" class="advads-manual-link">' . __( 'Manual', 'advanced-ads' ) . '</a>';
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* Ad Parameters.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin\Metaboxes;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Ad Parameters.
*/
class Ad_Parameters {
/**
* Get metabox id
*
* @return string
*/
public function get_box_id(): string {
return 'ad-parameters-box';
}
/**
* Hook into WordPress.
*
* @param Metabox_Ad $manager Manager instance.
*
* @return void
*/
public function register( $manager ): void {
add_meta_box(
$this->get_box_id(),
__( 'Ad Parameters', 'advanced-ads' ),
[ $manager, 'display' ],
Constants::POST_TYPE_AD,
'normal',
'high'
);
}
/**
* Get metaboxe view file
*
* @return string
*/
public function get_view(): string {
return ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-parameters.php';
}
/**
* Return manual link
*
* @return array|string
*/
public function get_handle_link() {
return '';
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Ad Targeting.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin\Metaboxes;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Ad Targeting.
*/
class Ad_Targeting {
/**
* Get metabox id
*
* @return string
*/
public function get_box_id(): string {
return 'ad-targeting-box';
}
/**
* Hook into WordPress.
*
* @param Metabox_Ad $manager Manager instance.
*
* @return void
*/
public function register( $manager ): void {
add_meta_box(
$this->get_box_id(),
__( 'Targeting', 'advanced-ads' ),
[ $manager, 'display' ],
Constants::POST_TYPE_AD,
'normal',
'default'
);
}
/**
* Get metaboxe view file
*
* @return string
*/
public function get_view(): string {
return ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-targeting.php';
}
/**
* Return manual link
*
* @return array|string
*/
public function get_handle_link() {
return [
'<a href="#" class="advads-video-link">' . __( 'Video', 'advanced-ads' ) . '</a>',
'<a href="https://wpadvancedads.com/manual/display-conditions/?utm_source=advanced-ads&utm_medium=link&utm_campaign=edit-display" target="_blank" class="advads-manual-link">' . __( 'Display Conditions', 'advanced-ads' ) . '</a>',
'<a href="https://wpadvancedads.com/manual/visitor-conditions/?utm_source=advanced-ads&utm_medium=link&utm_campaign=edit-visitor" target="_blank" class="advads-manual-link">' . __( 'Visitor Conditions', 'advanced-ads' ) . '</a>',
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* Ad Types.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin\Metaboxes;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Ad Types.
*/
class Ad_Types {
/**
* Get metabox id
*
* @return string
*/
public function get_box_id(): string {
return 'ad-types-box';
}
/**
* Hook into WordPress.
*
* @param Metabox_Ad $manager Manager instance.
*
* @return void
*/
public function register( $manager ): void {
add_meta_box(
$this->get_box_id(),
__( 'Ad Type', 'advanced-ads' ),
[ $manager, 'display' ],
Constants::POST_TYPE_AD,
'normal',
'high'
);
}
/**
* Get metaboxe view file
*
* @return string
*/
public function get_view(): string {
return ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-types.php';
}
/**
* Return manual link
*
* @return array|string
*/
public function get_handle_link() {
return '<a href="https://wpadvancedads.com/manual/ad-types?utm_source=advanced-ads&utm_medium=link&utm_campaign=edit-ad-type" target="_blank" class="advads-manual-link">' . __( 'Manual', 'advanced-ads' ) . '</a>';
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* Ad Usage.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Admin\Metaboxes;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Ad Usage.
*/
class Ad_Usage {
/**
* Get metabox id
*
* @return string
*/
public function get_box_id(): string {
return 'ad-usage-box';
}
/**
* Hook into WordPress.
*
* @param Metabox_Ad $manager Manager instance.
*
* @return void
*/
public function register( $manager ): void {
global $post;
if ( 'edit' === $post->filter ) {
add_meta_box(
$this->get_box_id(),
__( 'Usage', 'advanced-ads' ),
[ $manager, 'display' ],
Constants::POST_TYPE_AD,
'normal',
'high'
);
}
}
/**
* Get metaboxe view file
*
* @return string
*/
public function get_view(): string {
return ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-usage.php';
}
/**
* Return manual link
*
* @return array|string
*/
public function get_handle_link() {
return '';
}
}

View File

@@ -0,0 +1,219 @@
<?php
/**
* Ads edit screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use DateTimeImmutable;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Utilities\Params;
defined( 'ABSPATH' ) || exit;
/**
* Ads.
*/
class Ads_Editing extends Ads {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'ads-editing';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$this->set_hook( Constants::POST_TYPE_AD );
add_action( 'dbx_post_sidebar', [ $this, 'edit_form_end' ] );
add_action( 'edit_form_top', [ $this, 'edit_form_above_title' ] );
add_action( 'post_submitbox_misc_actions', [ $this, 'add_submit_box_meta' ] );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
$wp_screen = get_current_screen();
if ( 'post' === $wp_screen->base && 'add' !== $wp_screen->action ) {
add_action( 'advanced-ads-admin-header-actions', [ $this, 'add_new_ad_button' ] );
}
if ( 'post' === $wp_screen->base && Constants::POST_TYPE_AD === $wp_screen->post_type ) {
wp_advads()->registry->enqueue_script( 'screen-ads-editing' );
wp_advads()->registry->enqueue_style( 'screen-ads-editing' );
}
}
/**
* Define header args.
*
* @return array
*/
public function define_header_args(): array {
return [
'manual_url' => 'https://wpadvancedads.com/manual/first-ad/',
'show_filter_button' => false,
];
}
/**
* Add information below the ad edit form
*
* @since 1.7.3
*
* @param WP_Post $post WordPress Post object.
*
* @return void
*/
public function edit_form_end( $post ): void {
if ( Constants::POST_TYPE_AD !== $post->post_type ) {
return;
}
include_once ADVADS_ABSPATH . 'views/admin/ads/info-bottom.php';
}
/**
* Add information above the ad title
*
* @since 1.5.6
*
* @param object $post WordPress post type object.
*
* @return void
*/
public function edit_form_above_title( $post ): void {
if ( ! isset( $post->post_type ) || Constants::POST_TYPE_AD !== $post->post_type ) {
return;
}
// Highlight Dummy ad if this is the first ad.
if ( ! WordPress::get_count_ads() ) {
?>
<style>.advanced-ads-type-list-dummy {
font-weight: bold;
}</style>
<?php
}
// Display general and wizard information.
include_once ADVADS_ABSPATH . 'views/admin/ads/info-top.php';
// Dont show placement options if this is an ad translated with WPML since the placement might exist already.
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
$trid = apply_filters( 'wpml_element_trid', null, $post->ID );
$translations = apply_filters( 'wpml_get_element_translations', null, $trid, 'Advanced_Ads' );
if ( count( $translations ) > 1 ) {
return;
}
}
$ad = wp_advads_get_ad( $post->ID );
$placements = wp_advads_get_placements();
/**
* Display ad injection information after ad is created.
*
* Set `advanced-ads-ad-edit-show-placement-injection` to false if you want to prevent the box from appearing
*/
if ( 6 === Params::get( 'message', 0, FILTER_VALIDATE_INT ) && apply_filters( 'advanced-ads-ad-edit-show-placement-injection', true ) ) {
$latest_post = $this->get_latest_post();
include_once ADVADS_ABSPATH . 'admin/views/placement-injection-top.php';
}
}
/**
* Add meta values below submit box
*
* @since 1.3.15
*
* @param WP_Post $post WordPress post type object.
*
* @return void
*/
public function add_submit_box_meta( $post ): void {
global $wp_locale;
// Early bail!!
if ( Constants::POST_TYPE_AD !== $post->post_type ) {
return;
}
$ad = wp_advads_get_ad( $post->ID );
// Get time set for ad or current timestamp (both GMT).
$utc_ts = $ad->get_expiry_date() ?: current_time( 'timestamp', true ); // phpcs:ignore
$local_time = ( new \DateTimeImmutable( '@' . $utc_ts ) )->setTimezone( WordPress::get_timezone() );
[ $curr_year, $curr_month, $curr_day, $curr_hour, $curr_minute ] = explode( '-', $local_time->format( 'Y-m-d-H-i' ) );
$enabled = (int) ! empty( $ad->get_expiry_date() );
include ADVADS_ABSPATH . 'views/admin/ads/submitbox-meta.php';
}
/**
* Whether to start the wizard by default or not
*
* @since 1.7.4
*
* @return bool true, if wizard should start automatically
*/
private function start_wizard_automatically(): bool {
global $post;
$user_id = get_current_user_id();
if ( ! $user_id ) {
return true;
}
$hide_wizard = get_user_meta( $user_id, 'advanced-ads-hide-wizard', true );
// true the ad already exists, if the wizard was never started or closed.
return ( 'edit' !== $post->filter && ( ! $hide_wizard || 'false' === $hide_wizard ) ) ? true : false;
}
/**
* Whether to show the wizard welcome message or not
*
* @since 1.7.4
*
* @return bool true, if wizard welcome message should be displayed
*/
private function show_wizard_welcome(): bool {
global $post;
$user_id = get_current_user_id();
if ( ! $user_id ) {
return true;
}
$hide_wizard = get_user_meta( $user_id, 'advanced-ads-hide-wizard', true );
return ( ! $hide_wizard && 'edit' !== $post->filter ) ? true : false;
}
/**
* Load latest blog post
*
* @return WP_POST|null
*/
private function get_latest_post() {
$posts = wp_get_recent_posts( [ 'numberposts' => 1 ] );
return $posts ? $posts[0] : null;
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Ads screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use WP_Screen;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Admin\Ad_List_Table;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Ads.
*/
class Ads extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'ads';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$has_ads = WordPress::get_count_ads();
// Forward Ads link to new-ad page when there is no ad existing yet.
add_submenu_page(
ADVADS_SLUG,
__( 'Ads', 'advanced-ads' ),
__( 'Ads', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_edit_ads' ),
! $has_ads ? 'post-new.php?post_type=' . Constants::POST_TYPE_AD . '&new=new' : 'edit.php?post_type=' . Constants::POST_TYPE_AD
);
$this->set_hook( 'edit-' . Constants::POST_TYPE_AD );
add_action( 'current_screen', [ $this, 'load_placement_ui' ] );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_style( 'screen-ads-listing' );
wp_advads()->registry->enqueue_script( 'screen-ads-listing' );
}
/**
* Load list table
*
* @param WP_Screen $screen Current screen instance.
*
* @return void
*/
public function load_placement_ui( WP_Screen $screen ): void {
if ( 'edit-' . Constants::POST_TYPE_AD === $screen->id ) {
( new Ad_List_Table() )->hooks();
}
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Dashboard screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Dashboard.
*/
class Dashboard extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'dashboard';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$has_ads = WordPress::get_count_ads();
add_menu_page(
__( 'Dashboard', 'advanced-ads' ),
'Advanced Ads',
Conditional::user_cap( 'advanced_ads_see_interface' ),
ADVADS_SLUG,
[ $this, 'display' ],
$this->get_icon_svg(),
'58.74'
);
$hook = add_submenu_page(
ADVADS_SLUG,
__( 'Dashboard', 'advanced-ads' ),
__( 'Dashboard', 'advanced-ads' ),
Conditional::user_cap( $has_ads ? 'advanced_ads_edit_ads' : 'advanced_ads_see_interface' ),
ADVADS_SLUG,
$has_ads ? '' : [ $this, 'display' ]
);
$this->set_hook( $hook );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_style( 'screen-dashboard' );
wp_advads()->registry->enqueue_script( 'screen-dashboard' );
}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {
include ADVADS_ABSPATH . 'views/admin/screens/dashboard.php';
}
/**
* Return Advanced Ads logo in base64 format for use in WP Admin menu. * Highlights the 'Advanced Ads->Ads' item in the menu when an ad edit page is open
*
* @return string
*/
private function get_icon_svg(): string {
return 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE4LjEuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJFYmVuZV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDY0Ljk5MyA2NS4wMjQiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDY0Ljk5MyA2NS4wMjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIHN0eWxlPSJmaWxsOiNFNEU0RTQ7IiBkPSJNNDYuNTcxLDI3LjY0MXYyMy4xMzNIMTQuMjVWMTguNDUzaDIzLjExOGMtMC45NTYtMi4xODMtMS40OTQtNC41OS0xLjQ5NC03LjEyNg0KCWMwLTIuNTM1LDAuNTM4LTQuOTQyLDEuNDk0LTcuMTI0aC02Ljk1N0gwdjQ5LjQ5M2wxLjYxOCwxLjYxOEwwLDUzLjY5NmMwLDYuMjU2LDUuMDY4LDExLjMyNiwxMS4zMjQsMTEuMzI4djBoMTkuMDg3aDMwLjQxMlYyNy42MTENCgljLTIuMTkxLDAuOTY0LTQuNjA5LDEuNTA5LTcuMTU3LDEuNTA5QzUxLjE0MiwyOS4xMiw0OC43NDYsMjguNTg4LDQ2LjU3MSwyNy42NDF6Ii8+DQo8Y2lyY2xlIHN0eWxlPSJmaWxsOiM5ODk4OTg7IiBjeD0iNTMuNjY2IiBjeT0iMTEuMzI4IiByPSIxMS4zMjgiLz4NCjwvc3ZnPg0K';
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Groups screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Admin\Groups_List_Table;
defined( 'ABSPATH' ) || exit;
/**
* Groups.
*/
class Groups extends Screen {
/**
* Hold table object.
*
* @var null|Groups_List_Table
*/
private $list_table = null;
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'groups';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$hook = add_submenu_page(
ADVADS_SLUG,
__( 'Ad Groups & Rotations', 'advanced-ads' ),
__( 'Groups & Rotation', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_edit_ads' ),
ADVADS_SLUG . '-groups',
[ $this, 'display' ]
);
$this->set_hook( $hook );
add_action( 'in_admin_header', [ $this, 'get_list_table' ] );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_style( 'screen-groups-listing' );
wp_advads()->registry->enqueue_script( 'screen-groups-listing' );
}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {
$wp_list_table = $this->get_list_table();
include_once ADVADS_ABSPATH . 'views/admin/screens/groups.php';
}
/**
* Get list table object
*
* @return null|Groups_List_Table
*/
public function get_list_table() {
$screen = get_current_screen();
if ( 'advanced-ads_page_advanced-ads-groups' === $screen->id && null === $this->list_table ) {
wp_advads()->registry->enqueue_script( 'groups' );
$screen->taxonomy = Constants::TAXONOMY_GROUP;
$screen->post_type = Constants::POST_TYPE_AD;
$this->list_table = new Groups_List_Table();
}
return $this->list_table;
}
}

View File

@@ -0,0 +1,266 @@
<?php
/**
* Onboarding wizard screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use AdvancedAds\Constants;
use Advanced_Ads_AdSense_Data;
use Advanced_Ads_AdSense_MAPI;
use Advanced_Ads_AdSense_Admin;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Onboarding Wizard.
*/
class Onboarding extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'onboarding';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$hook = add_submenu_page(
Constants::HIDDEN_PAGE_SLUG,
__( 'Onboarding Wizard', 'advanced-ads' ),
__( 'Onboarding Wizard', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_manage_options' ),
ADVADS_SLUG . '-onboarding',
[ $this, 'display' ]
);
$this->set_hook( $hook );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
$this->i18n();
$this->adsense_data();
wp_enqueue_media();
wp_advads()->registry->enqueue_style( 'screen-onboarding' );
wp_advads()->registry->enqueue_script( 'screen-onboarding' );
}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {
include ADVADS_ABSPATH . 'views/admin/screens/onboarding.php';
}
/**
* Add Adsense data
*
* @return void
*/
private function adsense_data(): void {
if ( current_user_can( Conditional::user_cap( 'advanced_ads_manage_options' ) ) ) {
$nonce = wp_create_nonce( 'advanced_ads_wizard' );
wp_advads()->json->add(
'wizard',
[
'nonce' => $nonce,
'authUrl' => 'https://accounts.google.com/o/oauth2/v2/auth?scope=' . rawurlencode( 'https://www.googleapis.com/auth/adsense.readonly' ),
'clientId' => Advanced_Ads_AdSense_MAPI::CID,
'state' => base64_encode( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
wp_json_encode(
[
'api' => 'adsense',
'nonce' => $nonce,
'return_url' => admin_url( 'admin.php?page=advanced-ads-onboarding&route=adsense#wizard#adsense' ),
]
)
),
'redirectUri' => Advanced_Ads_AdSense_MAPI::REDIRECT_URI,
'adsenseData' => array_merge(
Advanced_Ads_AdSense_Data::get_instance()->get_options(),
[ 'accounts' => Advanced_Ads_AdSense_MAPI::get_option()['accounts'] ]
),
'newAccountLink' => Advanced_Ads_AdSense_Admin::ADSENSE_NEW_ACCOUNT_LINK,
]
);
}
}
/**
* Add wizard internationalization
*
* @return void
*/
private function i18n(): void {
wp_advads_json_add(
'i18n',
[
'wizard' => [
'loading' => __( 'Loading...', 'advanced-ads' ),
'processing' => __( 'Processing authorization...', 'advanced-ads' ),
'selectAccount' => [
'optionZero' => __( 'Select an account', 'advanced-ads' ),
'title' => __( 'Please select an account to use', 'advanced-ads' ),
],
'exitLabel' => __( 'Exit the wizard without saving', 'advanced-ads' ),
'btnGoBack' => __( 'Go back', 'advanced-ads' ),
'newsletter' => [
'title' => __( 'Subscribe to our newsletter and get 2 add-ons for free', 'advanced-ads' ),
'btnLabel' => __( 'Subscribe now', 'advanced-ads' ),
'inputPlaceholder' => __( 'Enter your email address', 'advanced-ads' ),
],
'stepTitles' => [
'adImage' => __( 'Please select your image', 'advanced-ads' ),
'adCode' => __( 'Please paste your ad code', 'advanced-ads' ),
'congrats' => [
'default' => __( 'Congratulations, your ad is now published!', 'advanced-ads' ),
'adsenseManual' => __( 'Your ad is almost ready!', 'advanced-ads' ),
'adsenseAuto' => __( 'Congratulations, AdSense Auto Ads are now set up!', 'advanced-ads' ),
],
],
'firstStep' => [
'taskAdSense' => __( 'I want to use mostly Google AdSense or Google Auto Ads', 'advanced-ads' ),
'taskImage' => __( 'I want to add a banner ad with an image', 'advanced-ads' ),
'taskCode' => __( 'I want to insert an ad code from an ad network', 'advanced-ads' ),
'stepHeading' => __( 'Welcome! To kick things off, and to simplify your journey, answer a few questions and let Advanced Ads tailor the perfect ad for your site\'s needs.', 'advanced-ads' ),
'agreementText' => __( 'I agree to share usage data to <strong>help the developers</strong> improve the plugin. Read more in our <a href="https://wpadvancedads.com/privacy-policy/" target="_blank">privacy policy</a>.', 'advanced-ads' ),
'inputTitle' => __( 'What\'s your task?', 'advanced-ads' ),
],
'bannerAd' => [
'mediaFrameTitle' => __( 'Select an image to upload', 'advanced-ads' ),
'mediaFrameButton' => __( 'Use this image', 'advanced-ads' ),
'mediaBtnUpload' => __( 'Upload', 'advanced-ads' ),
'mediaBtnReplace' => __( 'Replace', 'advanced-ads' ),
'stepHeading' => __( 'Would you like to set a target URL for your image ad?', 'advanced-ads' ),
'inputPlaceholder' => __( 'Enter an optional target URL for your image ad', 'advanced-ads' ),
'footerEnableText' => __( 'Create placement and ad', 'advanced-ads' ),
'footerDisableText' => __( 'Please select an image', 'advanced-ads' ),
],
'codeAd' => [
'inputPlaceholder' => __( 'Paste the ad code that your advertising network has provided to you', 'advanced-ads' ),
'footerEnableText' => __( 'Insert the ad code into my site', 'advanced-ads' ),
'footerDisableText' => __( 'Please paste your ad code', 'advanced-ads' ),
],
'googleAd' => [
'adsPlacement' => [
[
'label' => __( 'I will place ad units manually', 'advanced-ads' ),
'value' => 'manual',
],
[
'label' => __( 'I will use Auto Ads and let Google place the ads automatically', 'advanced-ads' ),
'value' => 'auto_ads',
],
],
'autoAdsOptions' => [
[
'label' => __( 'Enable Auto Ads on my site', 'advanced-ads' ),
'value' => 'enable',
],
[
'label' => __( 'Enable Accelerated Mobile Pages (AMP) Auto Ads', 'advanced-ads' ),
'value' => 'enableAmp',
],
],
'errors' => [
'notSaved' => __( 'Unknown error while saving account information.', 'advanced-ads' ),
'notFetched' => __( 'Unknown error while fetching AdSense account information.', 'advanced-ads' ),
'notAuthorized' => __( 'Unknown error while submitting the authorization code.', 'advanced-ads' ),
],
'stepHeading' => __( 'Do you have a Google AdSense account?', 'advanced-ads' ),
'btnSignup' => __( 'No, Id like to sign up for free now', 'advanced-ads' ),
'btnConnect' => __( 'Yes, connect to AdSense now', 'advanced-ads' ),
'labelAccount' => __( 'Account holder name:', 'advanced-ads' ),
'labelConnected' => __( 'You are connected to Google AdSense. Publisher ID:', 'advanced-ads' ),
'labelAdsPlacement' => __( 'Will you place ad units manually or use Google Auto Ads?', 'advanced-ads' ),
'labelAutoAds' => __( 'Please confirm these Auto Ads options', 'advanced-ads' ),
'footerProcessText' => __( 'Process', 'advanced-ads' ),
'footerEnableText' => [
'manual' => __( 'Create placement and ad', 'advanced-ads' ),
'autoAds' => __( 'Confirm Auto Ads options', 'advanced-ads' ),
],
'footerDisableText' => __( 'Please select an option', 'advanced-ads' ),
],
'congrats' => [
'adsenseManual' => [
'stepHeading' => __( "For the last step, import the desired ad unit from AdSense. Visit the ad's edit screen to make your selection.", 'advanced-ads' ),
'btnEditItem' => __( 'Select ad unit', 'advanced-ads' ),
'liveHeading' => sprintf(
/* translators: 1: opening strong tag, 2: closing strong tag, 3: opening italic tag, 4: closing italic tag. */
esc_html__( 'We have created a placement for your ad that will display %1$safter the 3rd paragraph on every post%2$s. Go to %3$sAdvanced Ads > Placements%4$s and edit the placement to change this.', 'advanced-ads' ),
'<strong>',
'</strong>',
'<i>',
'</i>'
),
],
'adsenseAuto' => [
'stepHeading' => __( "Everything's ready for AdSense to populate your site with Auto Ads. Make sure your site is verified, <strong>enable Auto Ads in your AdSense account</strong>, and, optionally, fine-tune their settings further.", 'advanced-ads' ),
'btnAccount' => __( 'Go to AdSense account', 'advanced-ads' ),
],
'stepHeading' => __( 'We have created a placement for your ad that will display <strong>after the 3rd paragraph on every post</strong>. You may edit the placement to change this.', 'advanced-ads' ),
'liveHeading' => __( 'See the live ad in your website\'s frontend.', 'advanced-ads' ),
'btnEditItem' => __( 'Edit the placement', 'advanced-ads' ),
'btnLiveAd' => __( 'See the live ad', 'advanced-ads' ),
'upgradeHeading' => __( 'Upgrade to all features and full support today', 'advanced-ads' ),
'upgradeText' => __( 'Our All Access deal offers every drop of ad expertise that we\'ve acquired in more than ten years, distilled into one jam-packed plugin bundle, supported by a dedicated team of real persons eager to help you.', 'advanced-ads' ),
'btnUpgrade' => __( 'Upgrade now', 'advanced-ads' ),
'btnDashboard' => __( 'Go to the Dashboard', 'advanced-ads' ),
'upgradePoints' => [
[
'title' => __( 'More placements', 'advanced-ads' ),
'text' => __( 'to embed in high-converting spots', 'advanced-ads' ),
'icon' => true,
],
[
'title' => __( 'More conditions', 'advanced-ads' ),
'text' => __( 'for advanced targeting', 'advanced-ads' ),
'icon' => true,
],
[
'title' => __( 'Ad Tracking', 'advanced-ads' ),
'text' => __( 'to optimize performance', 'advanced-ads' ),
'icon' => true,
],
[
'title' => __( 'Click Fraud Protection', 'advanced-ads' ),
'text' => __( 'to safeguard your accounts', 'advanced-ads' ),
'icon' => true,
],
[
'title' => __( 'Lazy Loading', 'advanced-ads' ),
'text' => __( 'to speed up your website', 'advanced-ads' ),
'icon' => true,
],
[
'text' => '…' . __( 'and much more!', 'advanced-ads' ),
'icon' => false,
],
],
],
],
]
);
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* Placements screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use WP_Screen;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Admin\Placement_List_Table;
use AdvancedAds\Admin\Placement_Create_Modal;
defined( 'ABSPATH' ) || exit;
/**
* Placements.
*/
class Placements extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'placements';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$hook = add_submenu_page(
ADVADS_SLUG,
__( 'Ad Placements', 'advanced-ads' ),
__( 'Placements', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_manage_placements' ),
'edit.php?post_type=' . Constants::POST_TYPE_PLACEMENT
);
// Keep the manual placements page around, but redirect it to the custom post type.
$old_placements_hook = add_submenu_page(
'',
'',
'',
Conditional::user_cap( 'advanced_ads_manage_placements' ),
ADVADS_SLUG . '-placements',
'__return_true'
);
$this->set_hook( 'edit-' . Constants::POST_TYPE_PLACEMENT );
add_action( 'current_screen', [ $this, 'load_placement_ui' ] );
add_action( 'load-' . $old_placements_hook, [ $this, 'redirect_to_post_type' ] );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_style( 'screen-placements-listing' );
wp_advads()->registry->enqueue_script( 'screen-placements-listing' );
wp_advads_json_add( 'content_placement_picker_url', $this->get_content_placement_picker_url() );
}
/**
* Redirect old placement page to custom post type.
*
* @return void
*/
public function redirect_to_post_type(): void {
wp_safe_redirect( 'edit.php?post_type=' . Constants::POST_TYPE_PLACEMENT );
}
/**
* Load list table
*
* @param WP_Screen $screen Current screen instance.
*
* @return void
*/
public function load_placement_ui( WP_Screen $screen ): void {
if ( 'edit-' . Constants::POST_TYPE_PLACEMENT === $screen->id ) {
( new Placement_List_Table() )->hooks();
( new Placement_Create_Modal() )->hooks();
}
}
/**
* Get the URL where the user is redirected after activating the frontend picker for a "Content" placement.
*
* @return string
*/
private function get_content_placement_picker_url() {
$location = false;
if ( get_option( 'show_on_front' ) === 'posts' ) {
$recent_posts = wp_get_recent_posts(
[
'numberposts' => 1,
'post_type' => 'post',
'post_status' => 'publish',
],
'OBJECT'
);
if ( $recent_posts ) {
$location = get_permalink( $recent_posts[0] );
}
}
return $location ?? home_url();
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* Admin Pages Settings.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Admin Pages Settings.
*/
class Settings extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'settings';
}
/**
* Get the order number of the screen.
*
* @return int
*/
public function get_order(): int {
return 20;
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$hook = add_submenu_page(
ADVADS_SLUG,
__( 'Advanced Ads Settings', 'advanced-ads' ),
__( 'Settings', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_manage_options' ),
ADVADS_SLUG . '-settings',
[ $this, 'display' ]
);
$this->set_hook( $hook );
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_style( 'screen-settings' );
wp_advads()->registry->enqueue_script( 'screen-settings' );
}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {
include_once ADVADS_ABSPATH . 'views/admin/screens/settings.php';
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* Tools screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Admin\Pages;
use AdvancedAds\Abstracts\Screen;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Tools.
*/
class Tools extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'tools';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$hook = add_submenu_page(
ADVADS_SLUG,
__( 'Tools', 'advanced-ads' ),
__( 'Tools', 'advanced-ads' ),
Conditional::user_cap( 'advanced_ads_manage_options' ),
ADVADS_SLUG . '-tools',
[ $this, 'display' ]
);
$this->set_hook( $hook );
$this->set_tabs(
[
'importers' => [
'label' => __( 'Import & Export', 'advanced-ads' ),
'filename' => 'views/admin/tools/importers.php',
],
'version' => [
'label' => __( 'Version Control', 'advanced-ads' ),
'filename' => 'views/admin/tools/version.php',
],
]
);
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_script( 'admin-common' );
wp_advads()->registry->enqueue_style( 'screen-tools' );
wp_advads()->registry->enqueue_script( 'screen-tools' );
}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {
include_once ADVADS_ABSPATH . 'views/admin/screens/tools.php';
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Admin Pages UI Toolkit.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Admin\Pages;
use AdvancedAds\Abstracts\Screen;
defined( 'ABSPATH' ) || exit;
/**
* UI Toolkit Screen.
*/
class Ui_Toolkit extends Screen {
/**
* Screen unique id.
*
* @return string
*/
public function get_id(): string {
return 'ui-toolkit';
}
/**
* Register screen into WordPress admin area.
*
* @return void
*/
public function register_screen(): void {
$hook = add_submenu_page(
ADVADS_SLUG,
__( 'Advanced Ads Ui Toolkit', 'advanced-ads' ),
__( 'Ui Toolkit', 'advanced-ads' ),
'manage_options',
ADVADS_SLUG . '-ui-toolkit',
[ $this, 'display' ]
);
$this->set_hook( $hook );
$this->set_tabs(
[
'basic' => [
'label' => __( 'Basic', 'advanced-ads' ),
'filename' => 'views/ui-toolkit/basic.php',
],
'forms' => [
'label' => __( 'Forms', 'advanced-ads' ),
'filename' => 'views/ui-toolkit/forms.php',
],
'advanced' => [
'label' => __( 'Advanced', 'advanced-ads' ),
'filename' => 'views/ui-toolkit/advanced.php',
],
]
);
}
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {
wp_advads()->registry->enqueue_style( 'common' );
}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {
include_once ADVADS_ABSPATH . 'views/admin/screens/ui-toolkit.php';
}
}

View File

@@ -0,0 +1,108 @@
<?php
/**
* Bulk edit for placement
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 2.0
*/
namespace AdvancedAds\Admin\Placement;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Placement;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Placement Bulk Edit.
*/
class Bulk_Edit implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'bulk_edit_custom_box', [ $this, 'add_bulk_edit_fields' ], 10, 2 );
add_action( 'save_post', [ $this, 'save_bulk_edit' ], 100 );
}
/**
* Add the bulk edit inputs
*
* @param string $column_name the current column.
* @param string $post_type the current post type.
*
* @return void
*/
public function add_bulk_edit_fields( $column_name, $post_type ) {
// Early bail!!
if ( Constants::POST_TYPE_PLACEMENT !== $post_type || 'type' !== $column_name ) {
return;
}
include ADVADS_ABSPATH . 'views/admin/placements/bulk-edit.php';
/**
* Allow add-ons to add more fields.
*/
do_action( 'advanced-ads-placement-bulk-edit-fields' );
}
/**
* Save changes made during bulk edit
*
* @return void
*/
public function save_bulk_edit() {
// not placement or not enough permissions.
if ( Constants::POST_TYPE_PLACEMENT !== sanitize_key( Params::get( 'post_type' ) ) || ! current_user_can( 'advanced_ads_edit_ads' )
) {
return;
}
check_admin_referer( 'bulk-posts' );
$ad_label = Params::get( 'ad_label' );
$has_change = ! empty( $ad_label );
/**
* Filter to determine if there are changes to be saved during bulk edit.
*
* @param bool $has_change Indicates if there are changes to be saved.
*/
$has_change = apply_filters( 'advanced-ads-placement-bulk-edit-has-change', $has_change );
// No changes, bail out.
if ( ! $has_change ) {
return;
}
$placements = array_map(
function ( $placement ) {
return wp_advads_get_placement( absint( $placement ) );
},
wp_unslash( Params::get( 'post', [], FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ) )
);
foreach ( $placements as $placement ) {
if ( ! empty( $ad_label ) ) {
$placement->set_prop( 'ad_label', $ad_label );
}
/**
* Allow add-on to bulk save placements.
*
* @param Placement $placement current placement being saved.
*/
$placement = apply_filters( 'advanced-ads-placement-bulk-edit-save', $placement );
$placement->save();
}
}
}