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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,262 @@
<?php
/**
* This class is serving as the base for admin tables and providing a foundation.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Abstracts;
use WP_Screen;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin List Table.
*/
abstract class Admin_List_Table implements Integration_Interface {
/**
* Object being shown on the row.
*
* @var object|null
*/
protected $object = null;
/**
* Object type.
*
* @var string
*/
protected $object_type = 'unknown';
/**
* Post type.
*
* @var string
*/
protected $list_table_type = '';
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
if ( $this->list_table_type ) {
// Columns.
add_filter( 'default_hidden_columns', [ $this, 'default_hidden_columns' ], 10, 2 );
add_filter( 'manage_' . $this->list_table_type . '_posts_columns', [ $this, 'define_columns' ] );
add_filter( 'manage_edit-' . $this->list_table_type . '_sortable_columns', [ $this, 'define_sortable_columns' ] );
add_action( 'manage_' . $this->list_table_type . '_posts_custom_column', [ $this, 'render_columns' ], 10, 2 );
// Views.
add_filter( 'view_mode_post_types', [ $this, 'disable_view_mode' ] );
add_action( 'restrict_manage_posts', [ $this, 'restrict_manage_posts' ] );
// Query.
add_filter( 'request', [ $this, 'request_query' ] );
add_filter( 'admin_body_class', [ $this, 'add_body_class' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ], 11 );
}
}
/**
* Add a custom class to the body tag of Advanced Ads post type lists.
*
* @param string $classes Space-separated class list.
*
* @return string
*/
public function add_body_class( string $classes ): string {
$screen = get_current_screen();
if ( isset( $screen->id ) && 'edit-' . $this->list_table_type === $screen->id ) {
$classes .= ' advanced-ads-post-type-list';
}
return $classes;
}
/**
* Custom scripts and styles for the ad list page
*
* @return void
*/
public function enqueue_scripts(): void {
global $wp_query;
// show label before the search form if this is a search.
if ( Params::get( 's' ) ) {
wp_advads()->registry->inline_style(
'admin',
"
.post-type-{$this->list_table_type} .search-box:before { content: '" . esc_html__( 'Showing search results for', 'advanced-ads' ) . "'; float: left; margin-right: 8px; line-height: 30px; font-weight: 700; }
.post-type-{$this->list_table_type} .subtitle { display: none; }
"
);
}
// Adjust search form when there are no results.
if ( Conditional::has_filter_or_search() && 0 === $wp_query->found_posts ) {
wp_advads()->registry->inline_style( 'admin', ".post-type-{$this->list_table_type} .search-box { display: block !important; }" );
return;
}
// Show filters, if the option to show them is enabled or a search is running.
if ( get_current_screen()->get_option( 'show-filters' ) || Conditional::has_filter_or_search() ) {
wp_advads()->registry->inline_style( 'admin', ".post-type-{$this->list_table_type} .search-box { display: block !important; }" );
if ( isset( $wp_query->found_posts ) && $wp_query->found_posts > 0 ) {
wp_advads()->registry->inline_style( 'admin', ".post-type-{$this->list_table_type} .tablenav.top .alignleft.actions:not(.bulkactions) { display: block; }" );
}
}
}
/**
* Define which columns to show on this screen.
*
* @param array $columns Existing columns.
*
* @return array
*/
public function define_columns( $columns ): array {
return $columns;
}
/**
* Define which columns are sortable.
*
* @param array $columns Existing columns.
*
* @return array
*/
public function define_sortable_columns( $columns ): array {
return $columns;
}
/**
* Define hidden columns.
*
* @return array
*/
protected function define_hidden_columns(): array {
return [];
}
/**
* Adjust which columns are displayed by default.
*
* @param array $hidden Current hidden columns.
* @param WP_Screen $screen Current screen.
*
* @return array
*/
public function default_hidden_columns( $hidden, $screen ): array {
if ( isset( $screen->id ) && 'edit-' . $this->list_table_type === $screen->id ) {
$hidden = array_merge( $hidden, $this->define_hidden_columns() );
}
return $hidden;
}
/**
* 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 {}
/**
* Render individual columns.
*
* @param string $column Column ID to render.
* @param int $post_id Post ID being shown.
*
* @return void
*/
public function render_columns( $column, $post_id ): void {
$this->prepare_row_data( $post_id );
if ( ! $this->object ) {
return;
}
if ( is_callable( [ $this, 'render_' . $column . '_column' ] ) ) {
$this->{"render_{$column}_column"}();
}
do_action( 'advanced-ads-ad-render-columns', $column, $this->object );
do_action( "advanced-ads-{$this->object_type}-render-column-{$column}", $this->object );
}
/**
* Removes this type from list of post types that support "View Mode" switching.
* View mode is seen on posts where you can switch between list or excerpt. Our post types don't support
* it, so we want to hide the useless UI from the screen options tab.
*
* @param array $post_types Array of post types supporting view mode.
*
* @return array Array of post types supporting view mode, without this type.
*/
public function disable_view_mode( $post_types ): array {
unset( $post_types[ $this->list_table_type ] );
return $post_types;
}
/**
* Render any custom filters and search inputs for the list table.
*
* @return void
*/
protected function render_filters(): void {}
/**
* See if we should render search filters or not.
*
* @return void
*/
public function restrict_manage_posts(): void {
global $typenow;
if ( $this->list_table_type === $typenow ) {
$this->render_filters();
}
}
/**
* Handle any filters.
*
* @param array $query_vars Query vars.
*
* @return array
*/
public function request_query( $query_vars ): array {
global $typenow;
if ( $this->list_table_type === $typenow ) {
return $this->query_filters( $query_vars );
}
return $query_vars;
}
/**
* Handle any custom filters.
*
* @param array $query_vars Query vars.
*
* @return array
*/
protected function query_filters( $query_vars ): array {
return $query_vars;
}
}

View File

@@ -0,0 +1,440 @@
<?php
/**
* This class is serving as the base for metadata service.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Abstracts;
use WP_Error;
use Exception;
use AdvancedAds\Framework\Utilities\Arr;
defined( 'ABSPATH' ) || exit;
/**
* Data.
*/
abstract class Data implements \ArrayAccess {
/**
* ID for this object.
*
* @var int
*/
protected $id = 0;
/**
* Core data for this object. Name value pairs (name + default value).
*
* @var array
*/
protected $data = [];
/**
* Core data changes for this object.
*
* @var array
*/
protected $changes = [];
/**
* Set to _data on construct so we can track and reset data if needed.
*
* @var array
*/
protected $default_data = [];
/**
* This is false until the object is read from the DB.
*
* @var bool
*/
protected $object_read = false;
/**
* This is the object type.
*
* @var string
*/
protected $object_type = 'post';
/**
* Contains a reference to the data store for this class.
*
* @var object
*/
protected $data_store;
/**
* Stores temp data.
*
* @var array
*/
protected $temp_data = [];
/**
* Change data to JSON format.
*
* @return string Data in JSON format.
*/
public function __toString() {
return wp_json_encode( $this->get_data() );
}
/**
* Default constructor.
*
* @param int|object|array $read ID to load from the DB (optional) or already queried data.
*/
public function __construct( $read = 0 ) { // phpcs:ignore
$this->default_data = $this->data;
}
/**
* Save should create or update based on object existence.
*
* @return int ID.
*/
public function save(): int {
// Early bail!!
if ( ! $this->data_store ) {
return $this->get_id();
}
if ( $this->get_id() ) {
$this->data_store->update( $this );
} else {
$this->data_store->create( $this );
}
return $this->get_id();
}
/**
* Delete an object, set the ID to 0, and return result.
*
* @param bool $force_delete Should the date be deleted permanently.
*
* @return bool
*/
public function delete( $force_delete = false ): bool {
if ( $this->data_store ) {
$this->data_store->delete( $this, $force_delete );
$this->set_id( 0 );
return true;
}
return false;
}
/**
* Merge changes with data and clear.
*
* @return void
*/
public function apply_changes(): void {
$this->data = array_replace_recursive( $this->data, $this->changes );
$this->changes = [];
}
/* Getter ------------------- */
/**
* Returns the unique ID for this object.
*
* @return int
*/
public function get_id(): int {
return $this->id;
}
/**
* Get the data store.
*
* @return object
*/
public function get_data_store() {
return $this->data_store;
}
/**
* Get object read property.
*
* @return bool
*/
public function get_object_read(): bool {
return (bool) $this->object_read;
}
/**
* Returns all data for this object.
*
* @return array
*/
public function get_data(): array {
return array_merge(
[ 'id' => $this->get_id() ],
$this->data
);
}
/**
* Returns array of expected data keys for this object.
*
* @return array
*/
public function get_data_keys() {
$keys = array_keys( $this->data );
$keys = array_diff( $keys, [ 'title', 'content', 'status' ] );
return array_merge( $keys );
}
/**
* Return data changes only.
*
* @return array
*/
public function get_changes(): array {
return $this->changes;
}
/**
* Prefix for action and filter hooks on data.
*
* @return string
*/
protected function get_hook_prefix(): string {
return 'advanced-ads-' . $this->object_type . '-get-';
}
/**
* Gets a prop for a getter method.
*
* Gets the value from either current pending changes, or the data itself.
* Context controls what happens to the value before it's returned.
*
* @param string $prop Name of prop to get.
* @param string $context What the value is for. Valid values are view and edit.
*
* @return mixed
*/
public function get_prop( $prop, $context = 'view' ) {
$value = null;
if ( Arr::has( $this->temp_data, $prop ) ) {
$value = Arr::get( $this->temp_data, $prop );
} elseif ( Arr::has( $this->data, $prop ) ) {
$value = Arr::has( $this->changes, $prop )
? Arr::get( $this->changes, $prop )
: Arr::get( $this->data, $prop );
}
if ( 'view' === $context ) {
/**
* Filter the option value retrieved for $field.
* `$field` parameter makes dynamic hook portion.
*
* @var mixed $value The option value (may be set to default).
* @var Ad $this The current Ad instance.
*/
$value = apply_filters_deprecated(
"advanced-ads-ad-option-{$prop}",
[ $value, $this ],
'2.0.0',
$this->get_hook_prefix() . $prop,
'This filter is deprecated. Use ' . $this->get_hook_prefix() . $prop . ' instead.'
);
$value = apply_filters( $this->get_hook_prefix() . $prop, $value, $this );
}
return $value;
}
/* Setter ------------------- */
/**
* Set ID.
*
* @param int $id ID.
*
* @return void
*/
public function set_id( $id ): void {
$this->id = absint( $id );
}
/**
* Set object read property.
*
* @param bool $read Should read?.
*
* @return void
*/
public function set_object_read( $read = true ): void {
$this->object_read = boolval( $read );
}
/**
* Set all props to default values.
*
* @return void
*/
public function set_defaults(): void {
$this->data = $this->default_data;
$this->changes = [];
$this->set_object_read( false );
}
/**
* Set a collection of props in one go, collect any errors, and return the result.
* Only sets using public methods.
*
* @param array $props Key value pairs to set. Key is the prop and should map to a setter function name.
*
* @return bool|WP_Error
*/
public function set_props( $props ) {
$errors = false;
foreach ( $props as $prop => $value ) {
try {
/**
* Checks if the prop being set is allowed, and the value is not null.
*/
if ( is_null( $value ) ) {
continue;
}
$setter = 'set_' . str_replace( '-', '_', $prop );
if ( is_callable( [ $this, $setter ] ) ) {
$this->{$setter}( $value );
} else {
$this->set_prop( $prop, $value );
}
} catch ( Exception $e ) {
if ( ! $errors ) {
$errors = new WP_Error();
}
$errors->add( $e->getCode(), $e->getMessage(), [ 'property_name' => $prop ] );
}
}
return $errors && count( $errors->get_error_codes() ) ? $errors : true;
}
/**
* Sets a prop for a setter method.
*
* @param string $prop Name of prop to set.
* @param mixed $value Value of the prop.
*
* @return void
*/
public function set_prop( $prop, $value ): void {
if ( Arr::has( $this->data, $prop ) && true === $this->object_read ) {
if ( Arr::get( $this->data, $prop ) !== $value ) {
Arr::set( $this->changes, $prop, $value );
}
} else {
Arr::set( $this->data, $prop, $value );
}
}
/**
* Sets a prop temporary.
*
* @param string $prop Name of prop to set.
* @param mixed $value Value of the prop.
*
* @return void
*/
public function set_prop_temp( $prop, $value ): void {
$this->temp_data[ $prop ] = $value;
}
/**
* Unset a prop.
*
* @param string $prop Name of prop to unset.
*
* @return void
*/
public function unset_prop( $prop ): void {
if ( array_key_exists( $prop, $this->changes ) ) {
unset( $this->changes );
return;
}
$this->data[ $prop ] = null;
}
// ArrayAccess API -------------------.
/**
* Sets the value at the specified offset.
*
* @param mixed $offset The offset to set the value at.
* @param mixed $value The value to set.
*
* @return void
*/
public function offsetSet( $offset, $value ): void {
$this->set_prop( $offset, $value );
}
/**
* Checks if the given offset exists.
*
* @param mixed $offset The offset to check for existence.
*
* @return bool True if the offset exists, false otherwise.
*/
public function offsetExists( $offset ): bool {
$func = 'get_' . $offset;
if ( method_exists( $this, $func ) ) {
return null !== $this->$func();
}
return false;
}
/**
* Unsets the property at the specified offset.
*
* @param mixed $offset The offset to unset.
*
* @return void
*/
public function offsetUnset( $offset ): void {
$this->unset_prop( $offset );
}
/**
* Retrieve the value at the specified offset.
*
* This method attempts to call a getter method based on the offset name.
* If a method named 'get_{offset}' exists, it will be called and its return value will be returned.
* If no such method exists, null will be returned.
*
* @param string $offset The offset to retrieve.
*
* @return mixed The value at the specified offset, or null if the method does not exist.
*/
#[\ReturnTypeWillChange]
public function offsetGet( $offset ) {
$func = 'get_' . $offset;
if ( method_exists( $this, $func ) ) {
return $this->$func();
}
return null;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Abstracts Factory.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Abstracts;
defined( 'ABSPATH' ) || exit;
/**
* Abstracts Factory.
*/
abstract class Factory {
/**
* Retrieves the classname for entity type.
*
* @param Types $manager The manager object.
* @param string $entity_type The entity type.
* @param string $default_type The entity default type.
*
* @return string The classname for the given entity type.
*/
public function get_classname( $manager, $entity_type, $default_type = 'default' ) {
// If the manager is called outside `init` hook, we need to initialize it.
if ( empty( $manager->get_types() ) || ! $manager->has_type( $default_type ) ) {
$manager->register_types();
}
$type = $manager->has_type( $entity_type )
? $manager->get_type( $entity_type )
: $manager->get_type( $default_type );
return $type->get_classname();
}
}

View File

@@ -0,0 +1,743 @@
<?php
/**
* This class is serving as the base for various group types and providing a foundation for defining common group attributes and methods.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Abstracts;
use Advanced_Ads;
use Advanced_Ads_Utils;
use Advanced_Ads_Inline_Css;
use AdvancedAds\Traits;
use AdvancedAds\Constants;
use AdvancedAds\Frontend\Stats;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Interfaces\Group_Type;
defined( 'ABSPATH' ) || exit;
/**
* Group.
*/
class Group extends Data {
use Traits\Entity;
use Traits\Wrapper;
/**
* This is the object type.
*
* @var string
*/
protected $object_type = 'group';
/**
* The label for the group.
*
* @var string|null
*/
private $label = null;
/**
* Core data for this object. Name value pairs (name + default value).
*
* @var array
*/
protected $data = [
'title' => '',
'content' => '',
'status' => false,
'slug' => '',
'type' => 'default',
'ad_count' => 1,
'options' => [],
'ad_weights' => [],
];
/**
* Hold ads within the group
*
* @var Ad[]
*/
private $ads = null;
/**
* Hold sorted ads within the group
*
* @var Ad[]
*/
private $sorted_ads = null;
/**
* Wrapper for the group.
*
* @var array|null
*/
private $wrapper = null;
/**
* Get the group if ID is passed, otherwise the group is new and empty.
*
* @param Group|WP_Term|int $group Group to init.
*/
public function __construct( $group = 0 ) {
parent::__construct();
$this->set_group_id( $group );
$this->data_store = wp_advads_get_group_repository();
if ( $this->get_id() > 0 ) {
$this->data_store->read( $this );
}
}
/**
* Set the group ID depending on what was passed.
*
* @param Group|WP_Term|int $group Group instance, term instance or numeric.
*
* @return void
*/
private function set_group_id( $group ): void {
if ( is_numeric( $group ) && $group > 0 ) {
$this->set_id( $group );
} elseif ( $group instanceof self ) {
$this->set_id( absint( $group->get_id() ) );
} elseif ( ! empty( $group->term_id ) ) {
$this->set_id( absint( $group->term_id ) );
} else {
$this->set_object_read( true );
}
}
/* Getter ------------------- */
/**
* Get name.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return string
*/
public function get_name( $context = 'view' ): string {
return $this->get_title( $context );
}
/**
* Get display ad count.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return int|string
*/
public function get_display_ad_count( $context = 'view' ) {
$value = $this->get_prop( 'ad_count', $context );
return 'all' === $value ? $value : absint( $value );
}
/**
* Get options.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return array
*/
public function get_options( $context = 'view' ): array {
return $this->get_prop( 'options', $context );
}
/**
* Get ad weights.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return array
*/
public function get_ad_weights( $context = 'view' ): array {
return $this->get_prop( 'ad_weights', $context );
}
/**
* Get max ad to show.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return int|string
*/
public function get_ad_count( $context = 'view' ) {
return $this->get_prop( 'ad_count', $context );
}
/**
* Get publish date
*
* @return string|bool
*/
public function get_publish_date() {
return $this->get_prop( 'publish_date' );
}
/**
* Get modified date
*
* @return string|bool
*/
public function get_modified_date() {
return $this->get_prop( 'modified_date' );
}
/* Setter ------------------- */
/**
* Set name.
*
* @param string $name Group name.
*
* @return void
*/
public function set_name( $name ): void {
$this->set_title( $name );
}
/**
* Set options.
*
* @param array $options Group options.
*
* @return void
*/
public function set_options( $options ): void {
$this->set_prop( 'options', $options );
}
/**
* Set ad_weights.
*
* @param array $ad_weights Group ad_weights.
*
* @return void
*/
public function set_ad_weights( $ad_weights ): void {
$this->set_prop( 'ad_weights', $ad_weights );
}
/**
* Set ad count.
*
* @param int $ad_count Group ad_count.
*
* @return void
*/
public function set_ad_count( $ad_count ): void {
$ad_count = 'all' === $ad_count ? 'all' : absint( $ad_count );
$this->set_prop( 'ad_count', $ad_count );
}
/**
* Set publish date
*
* @param string $date Publish Date.
*
* @return void
*/
public function set_publish_date( $date ): void {
$this->set_prop( 'publish_date', $date );
}
/**
* Set modified date
*
* @param string $date Modified Date.
*
* @return void
*/
public function set_modified_date( $date ): void {
$this->set_prop( 'modified_date', $date );
}
/* Conditional ------------------- */
/**
* Checks if the group is placed in head.
*
* @return bool
*/
public function is_head_placement(): bool {
return null !== $this->get_parent() && $this->get_parent()->is_type( 'header' );
}
/**
* Determines whether the group can be displayed.
*
* @return bool True if the group can be displayed, false otherwise.
*/
public function can_display(): bool {
return apply_filters( 'advanced-ads-can-display-group', true, $this->get_id(), $this );
}
/**
* Check if current user has the capability to edit the group taxonomy.
*
* @return bool
*/
public static function can_current_user_edit_group() {
global $tax;
$group_taxonomy = $tax ? $tax : get_taxonomy( Constants::TAXONOMY_GROUP );
return current_user_can( $group_taxonomy->cap->edit_terms );
}
/* Additional methods ------------------- */
/**
* Prepares the output for the group.
*
* @return string The prepared output.
*/
public function prepare_output(): string {
$ordered_ad_ids = $this->get_ordered_ad_ids();
$override = apply_filters( 'advanced-ads-ad-select-override-by-group', false, $this, $ordered_ad_ids, $this->get_data() );
if ( false !== $override ) {
return $override;
}
if ( empty( $ordered_ad_ids ) ) {
return '';
}
$ad_args = $this->get_prop( 'ad_args' );
$ad_output = $this->prepare_ad_output( $ordered_ad_ids );
$global_output = $ad_args['global_output'] ?? true;
// Maintain Stats.
if ( $global_output && $ad_output ) {
Stats::get()->add_entity( 'group', $this->get_id(), $this->get_title() );
}
$ad_output = apply_filters( 'advanced-ads-group-output-array', $ad_output, $this );
if ( [] === $ad_output || ! is_array( $ad_output ) ) {
return '';
}
$output_string = implode( '', $ad_output );
$wrapper = ! $this->is_head_placement() ? $this->create_wrapper() : [];
// Adds inline css to the wrapper.
if ( ! empty( $ad_args['inline-css'] ) && ! isset( $ad_args['is_top_level'] ) ) {
$inline_css = new Advanced_Ads_Inline_Css();
$wrapper = $inline_css->add_css( $wrapper, $ad_args['inline-css'], $global_output );
}
if ( ! $this->is_head_placement() && [] !== $wrapper ) {
$output_string = '<div' . Advanced_Ads_Utils::build_html_attributes( $wrapper ) . '>'
. $this->get_label()
. apply_filters( 'advanced-ads-output-wrapper-before-content-group', '', $this )
. $output_string
. apply_filters( 'advanced-ads-output-wrapper-after-content-group', '', $this )
. '</div>';
}
if ( ! empty( $ad_args['is_top_level'] ) && ! empty( $ad_args['placement_clearfix'] ) ) {
$output_string .= '<br style="clear: both; display: block; float: none;"/>';
}
return $output_string;
}
/**
* Get group type object
*
* @return Group_Type|bool
*/
public function get_type_object() {
if ( ! wp_advads_has_group_type( $this->get_type() ) ) {
wp_advads_create_group_type( $this->get_type() );
}
return wp_advads_get_group_type( $this->get_type() );
}
/**
* Get total ads attached.
*
* @return int
*/
public function get_ads_count(): int {
return count( $this->get_ad_weights() );
}
/**
* Get ads attached to the group
*
* @return Ad[]
*/
public function get_ads(): array {
if ( null !== $this->ads ) {
return $this->ads;
}
$this->ads = [];
$weights = $this->get_ad_weights();
foreach ( $weights as $ad_id => $weight ) {
$ad = wp_advads_get_ad( $ad_id );
if ( $ad ) {
$this->ads[ $ad_id ] = $ad;
$ad->set_prop( 'weight', $weight );
}
}
return $this->ads;
}
/**
* Get ads ids attached to the group
*
* @return array
*/
public function get_ads_ids(): array {
return array_keys( $this->get_ad_weights() );
}
/**
* Order the ad list by weight first and then by title.
*
* @return array<int, int>
*/
public function get_sorted_ads(): array {
if ( null !== $this->sorted_ads ) {
return $this->sorted_ads;
}
$this->sorted_ads = [];
foreach ( $this->get_ads() as $ad ) {
$this->sorted_ads[] = [
'id' => $ad->get_id(),
'title' => $ad->get_title(),
'weight' => $ad->get_weight() ?? Constants::GROUP_AD_DEFAULT_WEIGHT,
];
}
array_multisort(
array_column( $this->sorted_ads, 'weight' ),
SORT_DESC,
array_column( $this->sorted_ads, 'title' ),
SORT_ASC,
$this->sorted_ads
);
$this->sorted_ads = array_combine( array_column( $this->sorted_ads, 'id' ), $this->sorted_ads );
return $this->sorted_ads;
}
/**
* Get the max weight for group depending on number of ads and default value
*
* @return int
*/
public function get_max_weight(): int {
$ads_count = $this->get_ads_count();
$max_weight = max( $ads_count, Constants::GROUP_AD_DEFAULT_WEIGHT );
return apply_filters( 'advanced-ads-max-ad-weight', $max_weight, $ads_count );
}
/**
* Get group hints
*
* @return array
*/
public function get_hints(): array {
$hints = [];
// Early bail!!
if ( ! Conditional::has_cache_plugins() || $this->get_ads_count() < 2 ) {
return $hints;
}
if ( ! class_exists( 'Advanced_Ads_Pro' ) ) {
$installed_plugins = get_plugins();
$link = 'https://wpadvancedads.com/add-ons/advanced-ads-pro/?utm_source=advanced-ads&utm_medium=link&utm_campaign=groups-CB';
$link_title = __( 'Get this add-on', 'advanced-ads' );
if ( isset( $installed_plugins['advanced-ads-pro/advanced-ads-pro.php'] ) ) {
$link = wp_nonce_url( 'plugins.php?action=activate&amp;plugin=advanced-ads-pro/advanced-ads-pro.php', 'activate-plugin_advanced-ads-pro/advanced-ads-pro.php' );
$link_title = __( 'Activate now', 'advanced-ads' );
}
$hints['cache'] = sprintf(
wp_kses(
/* translators: %1$s is an URL, %2$s is a URL text */
__( 'It seems that a caching plugin is activated. Your ads might not rotate properly. The cache busting in Advanced Ads Pro will solve that. <a href="%1$s" target="_blank">%2$s.</a>', 'advanced-ads' ),
[
'a' => [
'href' => [],
'target' => [],
],
]
),
$link,
$link_title
);
}
/**
* Allows to add new hints.
*
* @param string[] $hints Existing hints (escaped strings).
* @param Group $group The group object.
*/
return apply_filters( 'advanced-ads-group-hints', $hints, $this );
}
/**
* Build html for group hints.
*
* @return string
*/
public function get_hints_html(): string {
$hints_html = '';
foreach ( $this->get_hints() as $hint ) {
$hints_html .= '<p class="advads-notice-inline advads-error">' . $hint . '</p>';
}
return $hints_html;
}
/**
* Get the group edit link
*
* @return string
*/
public function get_edit_link() {
return add_query_arg(
[
'page' => 'advanced-ads-groups',
'advads-last-edited-group' => $this->get_id(),
],
admin_url( 'admin.php' ) . '#modal-group-edit-' . $this->get_id()
);
}
/**
* Get ordered ids of the ads that belong to the group
*
* @return array
*/
public function get_ordered_ad_ids() {
$ordered_ad_ids = $this->is_type( 'ordered' )
? $this->shuffle_ordered_ads()
: $this->shuffle_ads();
return apply_filters( 'advanced-ads-group-output-ad-ids', $ordered_ad_ids, $this->get_type(), $this->get_ads(), $this->get_ad_weights(), $this );
}
/**
* Shuffle ads based on ad weight.
*
* @since 1.0.0
*
* @return array
*/
public function shuffle_ads(): array {
$shuffled_ads = [];
$random_id = 0;
$ads = $this->get_ads();
$weights = $this->get_ad_weights();
while ( null !== $random_id ) {
$random_id = $this->get_random_ad_by_weight( $weights );
unset( $weights[ $random_id ] );
if ( ! empty( $ads[ $random_id ] ) ) {
$shuffled_ads[] = $random_id;
}
}
return $shuffled_ads;
}
/**
* Prepare the output for the ads in the group.
*
* @param array $ordered_ad_ids Ordered ad IDs.
*
* @return array Output for each ad.
*/
private function prepare_ad_output( $ordered_ad_ids ): array {
$output = [];
$ads_displayed = 0;
$ad_count = apply_filters( 'advanced-ads-group-ad-count', $this->get_ad_count(), $this );
if ( is_array( $ordered_ad_ids ) ) {
foreach ( $ordered_ad_ids as $ad_id ) {
$ad = wp_advads_get_ad( $ad_id );
if ( ! $ad ) {
continue;
}
$ad->set_prop_temp( 'ad_args', $this->get_prop( 'ad_args' ) );
$ad->set_parent( $this );
// group as head placement.
$ad->set_prop_temp( 'group_placement_context', $this->is_head_placement() ? 'header' : 'content' );
$ad->set_prop_temp( 'ad_label', 'disabled' );
$ad_output = $ad->can_display() ? $ad->output() : '';
if ( ! empty( $ad_output ) ) {
$output[] = $ad_output;
++$ads_displayed;
// Break the loop when maximum ads are reached.
if ( $ads_displayed === $ad_count ) {
break;
}
}
}
}
return $output;
}
/**
* Shuffles the ordered ads based on their weights.
*
* @return array
*/
private function shuffle_ordered_ads(): array {
$weights = $this->get_ad_weights();
// Sort the ad IDs by their weights.
arsort( $weights );
$ordered_ad_ids = array_keys( $weights );
$weights = array_values( $weights );
$count = count( $weights );
$pos = 0;
for ( $i = 1; $i <= $count; $i++ ) {
if ( $i === $count || $weights[ $i ] !== $weights[ $i - 1 ] ) {
$slice_len = $i - $pos;
if ( 1 !== $slice_len ) {
$shuffled = array_slice( $ordered_ad_ids, $pos, $slice_len );
shuffle( $shuffled );
array_splice( $ordered_ad_ids, $pos, $slice_len, $shuffled ); // Replace the unshuffled chunk with the shuffled one.
}
$pos = $i;
}
}
return $ordered_ad_ids;
}
/**
* Get random ad by ad weight.
*
* @since 1.0.0
*
* @source applied with fix for order http://stackoverflow.com/a/11872928/904614
*
* @param array $weights Array of $ad_id => weight pairs.
*
* @return null|int
*/
private function get_random_ad_by_weight( $weights ) {
// use maximum ad weight for ads without this
// ads might have a weight of zero (0); to avoid mt_rand fail assume that at least 1 is set.
$max = array_sum( $weights );
if ( $max < 1 ) {
return null;
}
$rand = wp_rand( 1, $max );
foreach ( $weights as $ad_id => $weight ) {
$rand -= $weight;
if ( $rand <= 0 ) {
return $ad_id;
}
}
return null;
}
/**
* Retrieves the label for the group.
*
* @return string The label for the group.
*/
private function get_label(): string {
if ( null === $this->label ) {
$ad_args = $this->get_prop( 'ad_args' );
$state = $ad_args['ad_label'] ?? 'default';
$this->label = Advanced_Ads::get_instance()->get_label( $this, $state );
}
return $this->label;
}
/**
* Creates a wrapper array for the group.
*
* @return array
*/
public function create_wrapper(): array {
// Early bail!!
if ( null !== $this->wrapper ) {
return $this->wrapper;
}
$this->wrapper = [];
$ad_args = $this->get_prop( 'ad_args' );
if ( ! isset( $ad_args['is_top_level'] ) ) {
$ad_args['is_top_level'] = true;
}
if ( $ad_args['is_top_level'] ) {
// Add placement class.
if ( ! empty( $ad_args['output']['class'] ) && is_array( $ad_args['output']['class'] ) ) {
$this->wrapper['class'] = $ad_args['output']['class'];
}
// Ad Health Tool add class wrapper.
if ( Conditional::user_can( 'advanced_ads_edit_ads' ) ) {
$frontend_prefix = wp_advads()->get_frontend_prefix();
$this->wrapper['class'][] = $frontend_prefix . 'highlight-wrapper';
}
if ( isset( $ad_args['output']['wrapper_attrs'] ) && is_array( $ad_args['output']['wrapper_attrs'] ) ) {
foreach ( $ad_args['output']['wrapper_attrs'] as $key => $value ) {
$this->wrapper[ $key ] = $value;
}
}
if ( ! empty( $ad_args['placement_position'] ) ) {
switch ( $ad_args['placement_position'] ) {
case 'left':
$this->wrapper['style']['float'] = 'left';
break;
case 'right':
$this->wrapper['style']['float'] = 'right';
break;
case 'center':
// We don't know whether the 'reserve_space' option exists and width is set.
$this->wrapper['style']['text-align'] = 'center';
break;
}
}
}
$this->wrapper = (array) apply_filters( 'advanced-ads-output-wrapper-options-group', $this->wrapper, $this );
if ( ( [] !== $this->wrapper || $this->get_label() ) && ! isset( $this->wrapper['id'] ) ) {
$this->wrapper['id'] = wp_advads()->get_frontend_prefix() . wp_rand();
}
return $this->wrapper;
}
}

View File

@@ -0,0 +1,161 @@
<?php
/**
* This class is serving as the base for placement types.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Abstracts;
defined( 'ABSPATH' ) || exit;
/**
* Placement Type.
*/
class Placement_Type {
/**
* Hold allowed ads for cache purpose.
*
* @var array
*/
private $allowed_ads = null;
/**
* Hold allowed groups for cache purpose.
*
* @var array
*/
private $allowed_groups = null;
/**
* Apply filters on options by type id.
*
* @param array $options Options array.
*
* @return array
*/
protected function apply_filter_on_options( $options ): array {
$defaults = [
'show_position' => false,
'show_lazy_load' => false,
'uses_the_content' => false,
'amp' => false,
];
$options = wp_parse_args( $options, $defaults );
return apply_filters(
'advanced-ads-placement-' . $this->get_id() . '-options',
$options
);
}
/**
* Get allowed item array for dropdown
*
* @return array
*/
public function get_allowed_items(): array {
return [
'groups' => [
'label' => __( 'Ad Groups', 'advanced-ads' ),
'items' => $this->get_allowed_groups(),
],
'ads' => [
'label' => __( 'Ads', 'advanced-ads' ),
'items' => $this->get_allowed_ads(),
],
];
}
/**
* Get all allowed ads for this placement type.
*
* @return array
*/
public function get_allowed_ads(): array {
if ( null !== $this->allowed_ads ) {
return $this->allowed_ads;
}
$this->allowed_ads = [];
foreach ( wp_advads_get_all_ads() as $ad ) {
if ( $this->is_ad_type_allowed( $ad->get_type() ) ) {
$this->allowed_ads[ 'ad_' . $ad->get_id() ] = $ad->get_title() . ( 'draft' === $ad->get_status() ? ' (' . __( 'draft', 'advanced-ads' ) . ')' : '' );
}
}
return $this->allowed_ads;
}
/**
* Get all allowed groups for this placement type.
*
* @return array
*/
public function get_allowed_groups(): array {
if ( null !== $this->allowed_groups ) {
return $this->allowed_groups;
}
$this->allowed_groups = [];
$ads = wp_advads_get_all_ads();
$groups = wp_advads_get_all_groups();
foreach ( $groups as $group ) {
if ( ! $this->is_group_type_allowed( $group->get_type() ) ) {
continue;
}
// Check if group has allowed ads.
foreach ( $group->get_ads_ids() as $ad_id ) {
if ( array_key_exists( $ad_id, $ads ) && $this->is_ad_type_allowed( $ads[ $ad_id ]->get_type() ) ) {
$this->allowed_groups[ 'group_' . $group->get_id() ] = $group->get_name();
break;
}
}
}
return $this->allowed_groups;
}
/**
* Check if the provided ad type is allowed
*
* @param string $type Ad type.
*
* @return bool
*/
public function is_ad_type_allowed( $type ): bool {
return $this->is_entity_allowed( $type, 'ad' );
}
/**
* Check if the provided group type is allowed.
*
* @param string $type Group type.
*
* @return bool
*/
public function is_group_type_allowed( $type ): bool {
return $this->is_entity_allowed( $type, 'group' );
}
/**
* Abstraction of whether entity is allowed.
*
* @param string $type Type to check.
* @param string $entity Entity type i.e. `ad` or `group`.
*
* @return bool
*/
public function is_entity_allowed( $type, $entity ) {
$options = $this->get_options();
$allowed = $options[ 'allowed_' . $entity . '_types' ] ?? true;
return true === $allowed ? true : in_array( $type, $allowed, true );
}
}

View File

@@ -0,0 +1,406 @@
<?php
/**
* This class is serving as the base for various placement types and providing a foundation for defining common placement attributes and methods.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Abstracts;
use RuntimeException;
use AdvancedAds\Traits;
use AdvancedAds\Constants;
use AdvancedAds\Frontend\Stats;
use AdvancedAds\Framework\Utilities\Formatting;
defined( 'ABSPATH' ) || exit;
/**
* Placement.
*/
class Placement extends Data {
use Traits\Entity;
use Traits\Wrapper;
/**
* This is the object type.
*
* @var string
*/
protected $object_type = 'placement';
/**
* This is the item object.
*
* @var Ad|Group|null
*/
protected $item_object = null;
/**
* This is the item type.
*
* @var string
*/
protected $item_type = '';
/**
* Core data for this object. Name value pairs (name + default value).
*
* @var array
*/
protected $data = [
'title' => '',
'content' => '',
'type' => 'default',
'slug' => '',
'status' => '',
'item' => '',
'display' => [],
'visitors' => [],
];
/**
* Get the placement if ID is passed, otherwise the placement is new and empty.
*
* @param Placement|WP_Post|int $placement Placement to init.
*/
public function __construct( $placement = 0 ) {
parent::__construct();
$this->set_placement_id( $placement );
$this->data_store = wp_advads_get_placement_repository();
if ( $this->get_id() > 0 ) {
$this->data_store->read( $this );
}
}
/**
* Set the placement ID depending on what was passed.
*
* @param Placement|WP_Post|int $placement Placement instance, post instance or numeric.
*
* @return void
*/
private function set_placement_id( $placement ): void {
if ( is_numeric( $placement ) && $placement > 0 ) {
$this->set_id( $placement );
} elseif ( $placement instanceof self ) {
$this->set_id( absint( $placement->get_id() ) );
} elseif ( ! empty( $placement->ID ) ) {
$this->set_id( absint( $placement->ID ) );
} else {
$this->set_object_read( true );
}
}
/* Getter ------------------- */
/**
* Get item.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return string
*/
public function get_item( $context = 'view' ): string {
return $this->get_prop( 'item', $context );
}
/**
* Get the display conditions for the placement.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return array
*/
public function get_display_conditions( $context = 'view' ): array {
return $this->get_prop( 'display', $context );
}
/**
* Get the visitor conditions for the placement.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return array
*/
public function get_visitor_conditions( $context = 'view' ): array {
return $this->get_prop( 'visitors', $context );
}
/* Setter ------------------- */
/**
* Set item.
*
* @param string $item Placement item.
*
* @return void
*/
public function set_item( $item ): void {
$this->set_prop( 'item', $item );
}
/**
* Set display conditions.
*
* @param string $conditions Placement conditions.
*
* @return void
*/
public function set_display_conditions( $conditions ): void {
$this->set_prop( 'display', $conditions );
}
/**
* Set conditions.
*
* @param string $conditions Placement conditions.
*
* @return void
*/
public function set_visitor_conditions( $conditions ): void {
$this->set_prop( 'visitors', $conditions );
}
/* Conditional ------------------- */
/**
* Check if placement item is allowed
*
* @return bool
*/
public function is_item_allowed(): bool {
$item_type = $this->get_item_type();
$item_object = $this->get_item_object();
return $this->get_type_object()
->is_entity_allowed( $item_object->get_type(), $item_type );
}
/**
* Determines whether the placement can be displayed.
*
* @return bool True if the placement can be displayed, false otherwise.
*/
public function can_display(): bool {
return apply_filters( 'advanced-ads-can-display-placement', true, $this->get_id(), $this );
}
/**
* Determines whether the placement has cache busting enabled.
*
* @return bool
*/
public function has_cache_busting(): bool {
$value = $this->get_prop( 'cache-busting' );
return 'auto' === $value || Formatting::string_to_bool( $value );
}
/* Additional Methods ------------------- */
/**
* Prepares the output for the placement.
*
* @return mixed The prepared output or the overridden return value.
*/
public function prepare_output(): string {
// Early bail!!
if ( empty( $this->get_item_object() ) || Constants::ENTITY_PLACEMENT === $this->get_item_type() || 'publish' !== $this->get_status() ) {
return '';
}
$prefix = wp_advads()->get_frontend_prefix();
$ad_args = $this->get_prop( 'ad_args' );
$item_type = $this->get_item_type();
// Inject options.
$this->item_object->set_prop_temp( 'ad_args', $ad_args );
if ( ! $this->is_type( 'header' ) ) {
$class = $ad_args['output']['class'] ?? [];
if ( ! in_array( $prefix . $this->get_slug(), $class, true ) ) {
$class[] = $prefix . $this->get_slug();
}
$ad_args['output'] = $ad_args['output'] ?? [];
$ad_args['output']['class'] = $class;
}
// Create placement id for various features like ajax ads.
$ad_args['output']['placement_id'] = $this->get_id();
// Inject options.
$this->item_object->set_prop_temp( 'ad_args', $ad_args );
$this->item_object->set_parent( $this );
$method = Constants::ENTITY_AD === $item_type ? 'ad' : 'group';
$args = wp_advads_default_entity_arguments( $method, $this->item_object->get_id(), $ad_args );
$overridden_entity = Constants::ENTITY_AD === $item_type
? apply_filters( 'advanced-ads-ad-select-override-by-ad', false, $this->item_object, $args )
: apply_filters( 'advanced-ads-ad-select-override-by-group', false, $this->item_object, $this->item_object->get_ordered_ad_ids(), $args );
if ( false !== $overridden_entity ) {
return $overridden_entity;
}
$output = $this->item_object->can_display() ? $this->item_object->output() : '';
// Maintain Stats.
if ( $output ) {
Stats::get()->add_entity( 'placement', $this->get_id(), $this->get_title() );
Stats::get()->add_entity( $this->get_item_type(), $this->item_object->get_id(), $this->item_object->get_title(), $this->get_id() );
}
return $output;
}
/**
* Update placement item.
*
* @param string $new_item Placement item id.
*
* @throws \RuntimeException If the new item is equal to the old item or if the item type is not allowed for the placement type.
*
* @return mixed
*/
public function update_item( $new_item ) {
list( 'type' => $item_type, 'id' => $item_id ) = $this->get_item_parts( $this->get_item() );
list( 'type' => $new_item_type, 'id' => $new_item_id ) = $this->get_item_parts( $new_item );
if ( $new_item_id === $item_id ) {
throw new RuntimeException( 'New item is equal to old item.' );
}
$new_item_object = 'ad' === $new_item_type
? wp_advads_get_ad( $new_item_id )
: wp_advads_get_group( $new_item_id );
$is_allowed = $this->get_type_object()
->is_entity_allowed( $new_item_object->get_type(), $new_item_type );
if ( ! $is_allowed ) {
throw new RuntimeException(
sprintf(
/* translators: 1: Entity type 2: Item type 3: Placement title */
esc_html__( '%1$s type "%2$s" not allowed for placement type "%3$s"', 'advanced-ads' ),
strtoupper( $new_item_type ), // phpcs:ignore
$new_item_object->get_type(), // phpcs:ignore
$this->get_type_object()->get_title() // phpcs:ignore
)
);
}
if ( ! update_post_meta( $this->get_id(), 'item', $new_item ) ) {
throw new RuntimeException( 'Can\'t update item.' );
}
return $new_item_object;
}
/**
* Remove placement item.
*
* @return bool
*/
public function remove_item(): bool {
return delete_post_meta( $this->get_id(), 'item' );
}
/**
* Get placement item type
*
* @return string
*/
public function get_item_type(): string {
$this->get_item_object();
return $this->item_type;
}
/**
* Get placement item object
*
* @return Ad|Group|bool|null
*/
public function get_item_object() {
global $sitepress;
// Early bail!!
if ( empty( $this->get_item() ) ) {
return $this->item_object;
}
list( 'type' => $item_type, 'id' => $item_id ) = $this->get_item_parts( $this->get_item() );
if ( Constants::ENTITY_AD === $item_type && defined( 'ICL_SITEPRESS_VERSION' ) ) {
/**
* Deliver the translated version of an ad if set up with WPML.
* If an ad is not translated, show the ad in the original language when this is the selected option in the WPML settings.
*
* @source https://wpml.org/wpml-hook/wpml_object_id/
* @source https://wpml.org/forums/topic/backend-custom-post-types-page-overview-with-translation-options/
*/
$item_id = apply_filters( 'wpml_object_id', $item_id, 'advanced_ads', $sitepress->is_display_as_translated_post_type( 'advanced_ads' ) );
}
$this->item_type = $item_type;
$this->item_object = 'ad' === $item_type
? wp_advads_get_ad( $item_id )
: wp_advads_get_group( $item_id );
if ( $this->item_object ) {
$this->item_object->set_parent( $this );
}
return $this->item_object;
}
/**
* Get placement type object
*
* @return Placement_Type|bool
*/
public function get_type_object() {
if ( ! wp_advads_has_placement_type( $this->get_type() ) ) {
wp_advads_create_placement_type( $this->get_type() );
}
return wp_advads_get_placement_type( $this->get_type() );
}
/**
* Get the item parts
*
* @param string $item Placement item.
*
* @return array
*/
private function get_item_parts( $item ): array {
$item_parts = explode( '_', $item );
return [
'type' => trim( $item_parts[0] ),
'id' => absint( $item_parts[1] ?? 0 ),
];
}
/**
* Get the placement edit link
*
* @return string|null
*/
public function get_edit_link() {
return add_query_arg(
[
'post_type' => Constants::POST_TYPE_PLACEMENT,
],
admin_url( 'edit.php#modal-placement-edit-' . $this->get_id() )
);
}
}

View File

@@ -0,0 +1,177 @@
<?php
/**
* Abstracts Screen.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Abstracts;
use AdvancedAds\Framework\Utilities\Params;
defined( 'ABSPATH' ) || exit;
/**
* Abstracts Screen.
*/
abstract class Screen {
/**
* Hold hook.
*
* @var string
*/
private $hook = '';
/**
* Hold tabs.
*
* @var array
*/
private $tabs = [];
/**
* Get the hook.
*
* @return string
*/
public function get_hook(): string {
return $this->hook;
}
/**
* Set the hook.
*
* @param string $hook Hook to set.
*
* @return void
*/
public function set_hook( $hook ): void {
$this->hook = $hook;
}
/**
* Get the tabs.
*
* @return array
*/
public function get_tabs(): array {
return $this->tabs;
}
/**
* Set the tabs.
*
* @param array $tabs Array of screen tabs.
*
* @return void
*/
public function set_tabs( $tabs ): void {
$this->tabs = apply_filters( 'advanced-ads-screen-tabs-' . $this->get_id(), $tabs );
}
/**
* Screen unique id.
*
* @return string
*/
abstract public function get_id(): string;
/**
* Register screen into WordPress admin area.
*
* @return void
*/
abstract public function register_screen(): void;
/**
* Enqueue assets
*
* @return void
*/
public function enqueue_assets(): void {}
/**
* Display screen content.
*
* @return void
*/
public function display(): void {}
/**
* Get the order value.
*
* @return int The order value, which is 10.
*/
public function get_order(): int {
return 10;
}
/**
* Get current tab id.
*
* @return string
*/
public function get_current_tab_id(): string {
$first = current( array_keys( $this->tabs ) );
return Params::get( 'sub_page', $first );
}
/**
* Get admin page header
*
* @param array $args Arguments to be used in the template.
*
* @return void
*/
public function get_header( $args = [] ): void {
$args = wp_parse_args(
$args,
[
'manual_url' => '',
'screen' => get_current_screen(),
]
);
extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
include ADVADS_ABSPATH . 'views/admin/ui/header.php';
}
/**
* Render tabs menu
*
* @param array $args Arguments to be used in the template.
*
* @return void
*/
public function get_tabs_menu( $args = [] ): void { // phpcs:ignore
$tabs = $this->tabs;
$active = $this->get_current_tab_id();
include ADVADS_ABSPATH . 'views/admin/ui/header-tabs.php';
}
/**
* Render tabs content
*
* @param array $args Arguments to be used in the template.
*
* @return void
*/
public function get_tab_content( $args = [] ): void { // phpcs:ignore
$active = $this->get_current_tab_id();
echo '<div class="advads-tab-content">';
if ( isset( $this->tabs[ $active ]['callback'] ) ) {
call_user_func( $this->tabs[ $active ]['callback'] );
} elseif ( isset( $this->tabs[ $active ]['filename'] ) ) {
include ADVADS_ABSPATH . $this->tabs[ $active ]['filename'];
}
echo '</div>';
}
}

View File

@@ -0,0 +1,194 @@
<?php
/**
* Abstracts Types.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds\Abstracts;
use AdvancedAds\Framework\Utilities\Str;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Abstracts Types.
*/
abstract class Types implements Integration_Interface {
/**
* Hold types.
*
* @var array
*/
private $types = [];
/**
* Hook to filter types.
*
* @var string
*/
protected $hook = 'advanced-ads-none-types';
/**
* Class for unknown type.
*
* @var string
*/
protected $type_unknown = '';
/**
* Type interface to check.
*
* @var string
*/
protected $type_interface = '';
/**
* Check if has premium types.
*
* @var bool
*/
private $has_premium = null;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'init', [ $this, 'register_types' ], 25 );
}
/**
* Create missing type
*
* @param string $type Missing type.
*
* @return mixed
*/
public function create_missing( $type ) {
$this->types[ $type ] = new $this->type_unknown(
[
'id' => $type,
'title' => Str::capitalize( $type ),
]
);
return $this->types[ $type ];
}
/**
* Register custom type.
*
* @param string $classname Type class name.
*
* @return void
*/
public function register_type( $classname ): void {
$type = new $classname();
$this->types[ $type->get_id() ] = $type;
}
/**
* Register custom types.
*
* @return void
*/
public function register_types(): void {
$this->register_default_types();
/**
* Developers can add new types using this filter
*/
do_action( $this->hook . '-manager', $this );
$this->types = apply_filters( $this->hook, $this->types );
$this->validate_types();
}
/**
* Has type.
*
* @param string $type Type to check.
*
* @return bool
*/
public function has_type( $type ): bool {
return array_key_exists( $type, $this->types );
}
/**
* Get the registered type.
*
* @param string $type Type to get.
*
* @return mixed|bool
*/
public function get_type( $type ) {
return $this->types[ $type ] ?? false;
}
/**
* Get the registered types.
*
* @param bool $with_unknown Include unknown type placements.
*
* @return array
*/
public function get_types( $with_unknown = true ): array {
return $with_unknown ? $this->types : array_filter( $this->types, fn( $type ) => ! is_a( $type, $this->type_unknown ) );
}
/**
* Check if has premium types.
*
* @return bool
*/
public function has_premium(): bool {
if ( null !== $this->has_premium ) {
return $this->has_premium;
}
$this->has_premium = false;
foreach ( $this->get_types() as $type ) {
if ( $type->is_premium() ) {
$this->has_premium = true;
break;
}
}
return $this->has_premium;
}
/**
* Register default types.
*
* @return void
*/
abstract protected function register_default_types(): void;
/**
* Validate types by type interface
*
* @return void
*/
private function validate_types(): void {
// Early bail!!
if ( empty( $this->type_unknown ) || empty( $this->type_interface ) ) {
return;
}
foreach ( $this->types as $slug => $type ) {
if ( ! is_array( $type ) || is_a( $type, $this->type_interface ) ) {
continue;
}
$type['id'] = $slug;
$this->types[ $slug ] = new $this->type_unknown( $type );
}
}
}

View File

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

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();
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* This class is responsible to model content ads.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Interfaces\Ad_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Content ad.
*/
class Ad_Content extends Ad implements Ad_Interface {
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
$output = $this->get_content();
if ( isset( $GLOBALS['wp_embed'] ) ) {
$old_post = $GLOBALS['post'];
$GLOBALS['post'] = $this->get_id(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$output = $GLOBALS['wp_embed']->run_shortcode( $output );
$output = $GLOBALS['wp_embed']->autoembed( $output );
$GLOBALS['post'] = $old_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
$output = wptexturize( $output );
$output = convert_smilies( $output );
$output = convert_chars( $output );
$output = wpautop( $output );
$output = shortcode_unautop( $output );
$output = $this->do_shortcode( $output );
if ( defined( 'ADVADS_DISABLE_RESPONSIVE_IMAGES' ) && ADVADS_DISABLE_RESPONSIVE_IMAGES ) {
return $output;
}
// Make included images responsive, since WordPress 4.4, before WordPress 5.5.
if ( function_exists( 'wp_make_content_images_responsive' ) && ! function_exists( 'wp_filter_content_tags' ) ) {
return function_exists( 'wp_filter_content_tags' )
? wp_filter_content_tags( $output )
: wp_make_content_images_responsive( $output ); // phpcs:ignore WordPress.WP.DeprecatedFunctions.wp_make_content_images_responsiveFound
}
// Function wp_make_content_images_responsive has been deprecated with WordPress 5.5.
if ( function_exists( 'wp_filter_content_tags' ) ) {
return wp_filter_content_tags( $output );
}
return $output;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* This class is responsible to model dummy ads.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use Advanced_Ads;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Interfaces\Ad_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Dummy ad.
*/
class Ad_Dummy extends Ad implements Ad_Interface {
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
$style = '';
if ( strpos( $this->get_position(), 'center' ) === 0 ) {
$style .= 'display: inline-block;';
}
$style = '' !== $style ? 'style="' . $style . '"' : '';
$img = sprintf(
'<img src="%s" width="300" height="250" %s />',
esc_url( ADVADS_BASE_URL . 'public/assets/img/dummy.jpg' ),
$style
);
$url = $this->get_url();
if ( ! defined( 'AAT_VERSION' ) && $url ) {
$options = Advanced_Ads::get_instance()->options();
$target_blank = ! empty( $options['target-blank'] ) ? ' target="_blank"' : '';
$img = sprintf( '<a href="%s"%s aria-label="dummy">%s</a>', esc_url( $url ), $target_blank, $img );
}
// Add 'loading' attribute if applicable, available from WP 5.5.
if ( function_exists( 'wp_lazy_loading_enabled' ) && wp_lazy_loading_enabled( 'img', 'the_content' ) ) {
// Optimize image HTML tag with loading attributes based on WordPress filter context.
$img = WordPress::img_tag_add_loading_attr( $img, 'the_content' );
}
return $img;
}
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* The ad factory.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use Exception;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Abstracts\Factory;
defined( 'ABSPATH' ) || exit;
/**
* Ads Factory.
*/
class Ad_Factory extends Factory {
/**
* Create an empty ad object
*
* @param string $type Type of ad to create.
*
* @return Ad|bool Ad object or false if the ad type not found.
*/
public function create_ad( $type = 'dummy' ) {
$ad_type = wp_advads_get_ad_type( $type );
if ( ! $ad_type ) {
return false;
}
$classname = $ad_type->get_classname();
// Create ad.
$ad = new $classname( 0 );
$ad->set_type( $ad_type->get_id() );
return $ad;
}
/**
* Get the ad object.
*
* @param Ad|WP_Post|int|bool $ad_id Ad instance, post instance, numeric or false to use global $post.
* @param string $new_type Change type of ad.
*
* @return Ad|bool Ad object or false if the ad cannot be loaded.
*/
public function get_ad( $ad_id, $new_type = '' ) {
$ad_id = $this->get_ad_id( $ad_id );
if ( ! $ad_id ) {
return false;
}
$ad_type = '' !== $new_type ? $new_type : $this->get_ad_type( $ad_id );
$classname = $this->get_classname( wp_advads_get_ad_type_manager(), $ad_type, 'dummy' );
try {
return new $classname( $ad_id );
} catch ( Exception $e ) {
return false;
}
return new Ad_Content();
}
/**
* Get the type of the ad.
*
* @param int $ad_id Ad ID.
*
* @return string The type of the ad.
*/
private function get_ad_type( $ad_id ): string {
// Allow the overriding of the lookup in this function. Return the ad type here.
$override = apply_filters( 'advanced-ads-ad-type', false, $ad_id );
if ( $override ) {
return $override;
}
$options = get_post_meta( $ad_id, Ad_Repository::OPTION_METAKEY, true );
return $options['type'] ?? 'dummy';
}
/**
* Get the ad ID depending on what was passed.
*
* @param Ad|WP_Post|int|bool $ad Ad instance, post instance, numeric or false to use global $post.
*
* @return int|bool false on failure
*/
private function get_ad_id( $ad ) {
global $post;
if ( false === $ad && isset( $post, $post->ID ) && Constants::POST_TYPE_AD === get_post_type( $post->ID ) ) {
return absint( $post->ID );
}
if ( is_numeric( $ad ) ) {
return $ad;
}
if ( is_an_ad( $ad ) ) {
return $ad->get_id();
}
if ( ! empty( $ad->ID ) ) {
return $ad->ID;
}
return false;
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* Manage ad and group relationship.
*
* @since 2.0.7
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
*/
namespace AdvancedAds\Ads;
use AdvancedAds\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Ad group relation class.
*/
class Ad_Group_Relation {
/**
* Hold groups.
*
* @var array
*/
private $groups = [];
/**
* Create ad group relation.
*
* @param Ad $ad Ad object.
*
* @return void
*/
public function relate( $ad ): void {
$old_groups = get_post_meta( $ad->get_id(), Constants::AD_META_GROUP_IDS, true );
$new_groups = wp_get_object_terms( $ad->get_id(), Constants::TAXONOMY_GROUP, [ 'fields' => 'ids' ] );
if ( empty( $old_groups ) || ! is_array( $old_groups ) ) {
$old_groups = [];
}
$removed_terms = array_diff( $old_groups, $new_groups );
$added_terms = array_diff( $new_groups, $old_groups );
$this->handle_removed_terms( $removed_terms, $ad->get_id() );
$this->handle_added_terms( $added_terms, $ad->get_id() );
update_post_meta( $ad->get_id(), Constants::AD_META_GROUP_IDS, $new_groups );
}
/**
* Handles the removed terms for an ad.
*
* @param array $removed_terms An array of term IDs that have been removed.
* @param int $ad_id The ID of the ad.
*
* @return void
*/
private function handle_removed_terms( $removed_terms, $ad_id ): void {
foreach ( $removed_terms as $group_id ) {
$group = $this->get_group( $group_id );
if ( ! $group ) {
continue;
}
$weights = $group->get_ad_weights();
if ( isset( $weights[ $ad_id ] ) ) {
unset( $weights[ $ad_id ] );
$group->set_ad_weights( $weights );
$group->save();
}
}
}
/**
* Handles the added terms for an ad.
*
* @param array $added_terms An array of term IDs that have been added.
* @param int $ad_id The ID of the ad.
*
* @return void
*/
private function handle_added_terms( $added_terms, $ad_id ): void {
foreach ( $added_terms as $group_id ) {
$group = $this->get_group( $group_id );
if ( ! $group ) {
continue;
}
$weights = $group->get_ad_weights();
if ( ! isset( $weights[ $ad_id ] ) ) {
$weights[ $ad_id ] = Constants::GROUP_AD_DEFAULT_WEIGHT;
$group->set_ad_weights( $weights );
$group->save();
}
}
}
/**
* Get group by id and cache them.
*
* @param int $group_id Group id.
*
* @return Group|bool Group object or false if the group cannot be loaded.
*/
private function get_group( $group_id ) {
if ( ! isset( $this->groups[ $group_id ] ) ) {
$this->groups[ $group_id ] = wp_advads_get_group( $group_id );
}
return $this->groups[ $group_id ];
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* This class is responsible to model group ads.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Interfaces\Ad_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Group ad.
*/
class Ad_Group extends Ad implements Ad_Interface {
/**
* Get the group id for the ad.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return int
*/
public function get_group_id( $context = 'view' ): int {
return $this->get_prop( 'group_id', $context ) ?? 0;
}
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
if ( ! $this->get_group_id() ) {
return '';
}
// Disable the ad label for the ad group wrapper itself to avoid duplicate labels.
$ad_args = $this->get_prop( 'ad_args' ) ?? [];
$ad_args['ad_label'] = 'disabled';
return get_the_group( $this->get_group_id(), '', $ad_args );
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* This class is responsible to model image ads.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use Advanced_Ads;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Interfaces\Ad_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Image ad.
*/
class Ad_Image extends Ad implements Ad_Interface {
/**
* Get the image id for the ad.
*
* @param string $context What the value is for. Valid values are view and edit.
*
* @return int
*/
public function get_image_id( $context = 'view' ): int {
return $this->get_prop( 'image_id', $context ) ?? 0;
}
/**
* Set the image id.
*
* @param int|string $image_id Image id for ad.
*/
public function set_image_id( $image_id ) {
$this->set_prop( 'image_id', absint( $image_id ) );
}
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
$url = $this->get_url();
$image_id = $this->get_image_id();
ob_start();
$this->get_type_object()->create_image_tag( $image_id, $this );
$img = ob_get_clean();
if ( ! defined( 'AAT_VERSION' ) && $url ) {
$alt = trim( esc_textarea( get_post_meta( $image_id, '_wp_attachment_image_alt', true ) ) );
$aria_label = ! empty( $alt ) ? $alt : wp_basename( get_the_title( $image_id ) );
$options = Advanced_Ads::get_instance()->options();
$target_blank = ! empty( $options['target-blank'] ) ? ' target="_blank"' : '';
$img = sprintf( '<a href="%s"%s aria-label="%s">%s</a>', esc_url( $url ), $target_blank, $aria_label, $img );
}
return $img;
}
/**
* Pre save
*
* @param array $post_data Post data.
*
* @return void
*/
public function pre_save( $post_data ): void {
$image_id = absint( $post_data['image_id'] ?? 0 );
if ( $image_id ) {
$attachment = get_post( $image_id );
if ( $attachment && 0 === $attachment->post_parent ) {
wp_update_post(
[
'ID' => $image_id,
'post_parent' => $this->get_id(),
]
);
}
}
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* This class is responsible to model plain ads.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Interfaces\Ad_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Plain ad.
*/
class Ad_Plain extends Ad implements Ad_Interface {
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
$content = $this->get_content();
// Evaluate the code as PHP if setting was never saved or is allowed.
if ( $this->is_php_allowed() && Conditional::is_php_allowed() ) {
ob_start();
// phpcs:ignore Squiz.PHP.Eval.Discouraged -- this is specifically eval'd so allow eval here.
eval( '?>' . $content );
$content = ob_get_clean();
}
if ( ! is_string( $content ) ) {
return '';
}
/**
* Apply do_blocks if the content has block code
* works with WP 5.0.0 and later
*/
if ( function_exists( 'has_blocks' ) && has_blocks( $content ) ) {
$content = do_blocks( $content );
}
if ( $this->is_shortcode_allowed() ) {
$content = $this->do_shortcode( $content );
}
// Add 'loading' attribute if applicable, available from WP 5.5.
if (
function_exists( 'wp_lazy_loading_enabled' )
&& wp_lazy_loading_enabled( 'img', 'the_content' )
&& preg_match_all( '/<img\s[^>]+>/', $content, $matches )
) {
foreach ( $matches[0] as $image ) {
if ( strpos( $image, 'loading=' ) !== false ) {
continue;
}
// Optimize image HTML tag with loading attributes based on WordPress filter context.
$content = str_replace( $image, WordPress::img_tag_add_loading_attr( $image, 'the_content' ), $content );
}
}
return (
(
( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) ||
! Conditional::can_author_unfiltered_html( (int) get_post_field( 'post_author', $this->get_id() ) )
)
&& version_compare( $this->get_prop( 'last_save_version' ) ?? '0', '1.35.0', 'ge' )
)
? wp_kses_post( $content )
: $content;
}
}

View File

@@ -0,0 +1,502 @@
<?php
/**
* Ad Repository.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use WP_Query;
use Exception;
use AdvancedAds\Constants;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Admin\Metabox_Ad;
use AdvancedAds\Utilities\WordPress;
use AdvancedAds\Framework\Utilities\Arr;
use AdvancedAds\Framework\Utilities\Params;
use AdvancedAds\Framework\Utilities\Formatting;
defined( 'ABSPATH' ) || exit;
/**
* Ad Repository.
*
* phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found -- remove it later
*/
class Ad_Repository {
/**
* Ad options metakey
*
* @var string
*/
const OPTION_METAKEY = 'advanced_ads_ad_options';
/* CRUD Methods ------------------- */
/**
* Create a new ad in the database.
*
* @param Ad $ad Ad object.
*
* @return Ad
*/
public function create( &$ad ): Ad {
$id = wp_insert_post(
apply_filters(
'advanced-ads-new-ad-data',
[
'post_type' => Constants::POST_TYPE_AD,
'post_status' => $ad->get_status() ? $ad->get_status() : 'publish',
'post_author' => ! empty( $ad->get_author_id() ) ? $ad->get_author_id() : get_current_user_id(),
'post_title' => $ad->get_title() ? $ad->get_title() : __( 'New Ad', 'advanced-ads' ),
'post_content' => $ad->get_content() ? $ad->get_content() : __( 'New ad content goes here', 'advanced-ads' ),
'comment_status' => 'closed',
'ping_status' => 'closed',
],
$ad
),
true
);
if ( $id && ! is_wp_error( $id ) ) {
$ad->set_id( $id );
$this->update_post_meta( $ad );
$this->update_post_term( $ad );
$this->update_version( $ad );
$ad->apply_changes();
}
return $ad;
}
/**
* Read an ad from the database.
*
* @param Ad $ad Ad object.
* @throws Exception If invalid ad.
*
* @return void
*/
public function read( &$ad ): void {
$ad->set_defaults();
$post_object = get_post( $ad->get_id() );
if ( ! $ad->get_id() || ! $post_object || Constants::POST_TYPE_AD !== $post_object->post_type ) {
throw new Exception( esc_html__( 'Invalid ad.', 'advanced-ads' ) );
}
$ad->set_props(
[
'title' => $post_object->post_title,
'status' => $post_object->post_status,
'slug' => $post_object->post_name,
'content' => $post_object->post_content,
'author_id' => $post_object->post_author,
]
);
$this->read_ad_data( $ad );
$ad->set_object_read( true );
}
/**
* Update an existing ad in the database.
*
* @param Ad $ad Ad object.
*
* @return void
*/
public function update( &$ad ): void {
global $wpdb;
$changes = $ad->get_changes();
// Only update the post when the post data changes.
if ( array_intersect( [ 'title', 'status', 'content' ], array_keys( $changes ) ) ) {
$is_text_ad = $ad->is_type( [ 'plain', 'content' ] );
$post_data = [
'post_title' => $ad->get_title( 'edit' ),
'post_status' => $ad->get_status( 'edit' ) ? $ad->get_status( 'edit' ) : 'publish',
'post_type' => Constants::POST_TYPE_AD,
'post_content' => apply_filters(
'advanced-ads-pre-ad-save-' . $ad->get_type(),
$is_text_ad
? wp_unslash( $ad->get_content( 'edit' ) )
: apply_filters( 'content_save_pre', wp_unslash( $ad->get_content( 'edit' ) ) )
),
];
/**
* When updating this object, to prevent infinite loops, use $wpdb
* to update data, since wp_update_post spawns more calls to the
* save_post action.
*
* This ensures hooks are fired by either WP itself (admin screen save), or an update purely from CRUD.
*
* Use direct DB update for user-input ads to preserve literal content.
* Use wp_update_post for other ad types to maintain WordPress security standards.
*/
if ( doing_action( 'save_post' ) || $is_text_ad ) {
$GLOBALS['wpdb']->update( $GLOBALS['wpdb']->posts, $post_data, [ 'ID' => $ad->get_id() ] );
clean_post_cache( $ad->get_id() );
} else {
wp_update_post( array_merge( [ 'ID' => $ad->get_id() ], $post_data ) );
}
} else { // Only update post modified time to record this save event.
$wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->posts,
[
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql', 1 ),
],
[
'ID' => $ad->get_id(),
]
);
clean_post_cache( $ad->get_id() );
}
$this->update_post_meta( $ad );
$this->update_post_term( $ad );
$ad->apply_changes();
}
/**
* Delete an ad from the database.
*
* @param Ad $ad Ad object or Ad id.
* @param bool $force_delete Whether to bypass Trash and force deletion. Default false.
*
* @return void
*/
public function delete( &$ad, $force_delete = false ): void {
// Early bail!!
if ( ! $ad || ! $ad->get_id() ) {
return;
}
if ( $force_delete ) {
wp_delete_post( $ad->get_id(), true );
$ad->set_id( 0 );
} else {
wp_trash_post( $ad->get_id() );
$ad->set_status( 'trash' );
}
}
/* Finder Methods ------------------- */
/**
* Get an ad by its ID.
*
* @param int $id The ID of the ad to retrieve.
*
* @return Ad|null
*/
public function get_ad_by_id( $id ) {
return wp_advads_get_ad( $id );
}
/**
* Get ads belonging to a specific group.
*
* @param int $group_id The ID of the group.
*
* @return Ad[]
*/
public function get_ads_by_group_id( $group_id ): array {
$group = wp_advads_get_group( $group_id );
return $group->get_ads();
}
/**
* Get ads associated with a specific placement.
*
* @param int $placement_id The ID of the placement.
*
* @return array
*/
public function get_ads_by_placement_id( $placement_id ): array {
$placement = wp_advads_get_placement( $placement_id );
$item = $placement->get_item_object();
if ( is_a_group( $item ) ) {
return $item->get_ads();
}
if ( is_an_ad( $item ) ) {
return [ $item ];
}
return [];
}
/**
* Get ads of a specific type.
*
* @param string $type The type of ads to retrieve.
*
* @return array
*/
public function get_ads_by_type( $type ): array {
return [];
}
/**
* Get all ads object.
*
* @return array
*/
public function get_all_ads(): array {
static $advads_all_ads;
if ( isset( $advads_all_ads ) ) {
return $advads_all_ads;
}
$advads_all_ads = [];
foreach ( $this->get_ads_dropdown() as $post_id => $name ) {
$advads_all_ads[ $post_id ] = wp_advads_get_ad( $post_id );
}
return $advads_all_ads;
}
/**
* Get all ads as dropdown.
*
* @return array
*/
public function get_ads_dropdown(): array {
$query = $this->query(
[
'orderby' => 'title',
'order' => 'ASC',
],
true
);
return $query->have_posts() ? wp_list_pluck( $query->posts, 'post_title', 'ID' ) : [];
}
/**
* Query ads based on the provided arguments.
*
* @param array $args The arguments to customize the query.
* @param bool $improve_query Whether to improve the query speed.
*
* @return WP_Query The WP_Query object containing the results of the query.
*/
public function query( $args, $improve_query = false ): WP_Query {
$args = wp_parse_args(
$args,
[
'posts_per_page' => -1,
'post_status' => [ 'publish', 'future', 'draft' ],
]
);
// Strict mode.
$args['post_type'] = Constants::POST_TYPE_AD;
if ( $improve_query ) {
$args = WordPress::improve_wp_query( $args );
}
return new WP_Query( $args );
}
/* Additional Methods ------------------- */
/**
* Read ad data. Can be overridden by child classes to load other props.
*
* @param Ad $ad Ad object.
*
* @return void
*/
private function read_ad_data( &$ad ): void {
$post_meta_values = get_post_meta( $ad->get_id(), self::OPTION_METAKEY, true );
if ( empty( $post_meta_values ) || ! is_array( $post_meta_values ) ) {
$post_meta_values = [];
}
$post_meta_values = $this->migrate_values( $post_meta_values );
$display_conditions = $post_meta_values['conditions'] ?? [];
$visitor_conditions = $post_meta_values['visitors'] ?? [];
if ( ! Arr::accessible( $display_conditions ) ) {
$display_conditions = [];
}
if ( ! Arr::accessible( $visitor_conditions ) ) {
$visitor_conditions = [];
}
$ad->set_props( $post_meta_values );
$ad->set_props(
[
'display_conditions' => $display_conditions,
'visitor_conditions' => $visitor_conditions,
'has_weekdays' => $post_meta_values['weekdays']['enabled'] ?? false,
'weekdays' => $post_meta_values['weekdays']['day_indexes'] ?? [],
]
);
}
/**
* Update ad data. Can be overridden by child classes to load other props.
*
* @param Ad $ad Ad object.
*
* @return void
*/
private function update_post_meta( &$ad ): void {
$post_data = Metabox_Ad::get_post_data();
$ad->set_prop( 'last_save_version', ADVADS_VERSION );
// Pre save.
if ( method_exists( $ad, 'pre_save' ) ) {
$ad->pre_save( $post_data );
}
// Filters to manipulate options or add more to be saved.
do_action( 'advanced-ads-ad-pre-save', $ad, $post_data );
$meta_keys = $ad->get_data_keys();
$meta_keys = array_combine( $meta_keys, $meta_keys );
$meta_values = [];
foreach ( $meta_keys as $meta_key => $prop ) {
$value = method_exists( $ad, "get_$prop" )
? $ad->{"get_$prop"}( 'edit' )
: $ad->get_prop( $prop, 'edit' );
$value = is_string( $value ) ? wp_slash( $value ) : $value;
switch ( $prop ) {
case 'clearfix':
case 'allow_php':
case 'has_weekdays':
case 'reserve_space':
case 'allow_shortcodes':
$value = Formatting::bool_to_string( $value );
break;
case 'description':
$value = esc_textarea( $value );
break;
case 'display_conditions':
case 'visitor_conditions':
$value = WordPress::sanitize_conditions( $value );
if (
'editpost' === Params::post( 'originalaction' ) &&
! isset( $post_data[ $meta_key ] )
) {
$value = [];
}
break;
}
$meta_values[ $meta_key ] = $value;
}
// Convert values to array.
$meta_values['weekdays'] = [
'enabled' => $meta_values['has_weekdays'],
'day_indexes' => $meta_values['weekdays'],
];
unset( $meta_values['has_weekdays'] );
update_post_meta( $ad->get_id(), self::OPTION_METAKEY, $meta_values );
}
/**
* Update ad groups.
*
* @param Ad $ad Ad object.
*
* @return void
*/
private function update_post_term( &$ad ): void {
( new Ad_Group_Relation() )->relate( $ad );
}
/**
* Make sure we store the ad version (to track data changes).
*
* @param Ad $ad Ad object.
*
* @return void
*/
private function update_version( &$ad ): void {
if ( ! metadata_exists( 'post', $ad->get_id(), '_ad_version' ) ) {
update_post_meta( $ad->get_id(), '_ad_version', ADVADS_VERSION );
}
}
/**
* Migrate values to new version
*
* @param array $values Values to migrate.
*
* @return array
*/
private function migrate_values( $values ): array {
$output = wp_parse_args(
$values['output'] ?? [],
[
'position' => 'none',
'clearfix' => false,
'add_wrapper_sizes' => false,
'margin' => [
'top' => 0,
'left' => 0,
'bottom' => 0,
'right' => 0,
],
]
);
foreach ( $output as $key => $value ) {
if ( isset( $values[ $key ] ) ) {
continue;
}
$values[ $key ] = $value;
}
$values['reserve_space'] = $values['reserve_space'] ?? $output['add_wrapper_sizes'];
// Typecast the margin values.
$values['margin'] = array_map( 'intval', $values['margin'] );
// Old values are left, center and right, if none of these we've already migrated.
if ( ! in_array( $values['position'], [ 'left', 'center', 'right' ], true ) ) {
// Ensure we get an array with min two elements.
$position = explode( '_', $values['position'] . '_' );
// Explicitly set clearfix option.
$values['clearfix'] = 'center' !== $position[0] && 'nofloat' === $position[1];
} elseif ( 'center' === $values['position'] ) {
$values['position'] = 'center_nofloat';
} else {
$values['position'] .= $values['clearfix'] ? '_nofloat' : '_float';
}
if ( isset( $values['visitor'] ) && ! isset( $values['visitors'] ) ) {
$values['visitors'] = $values['visitor'];
}
unset( $values['visitor'], $values['output'] );
return $values;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* Ad types manager.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use AdvancedAds\Ads\Types\AMP;
use AdvancedAds\Ads\Types\GAM;
use AdvancedAds\Abstracts\Types;
use AdvancedAds\Ads\Types\Dummy;
use AdvancedAds\Ads\Types\Group;
use AdvancedAds\Ads\Types\Image;
use AdvancedAds\Ads\Types\Plain;
use AdvancedAds\Ads\Types\Content;
use AdvancedAds\Ads\Types\Unknown;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Ad Types.
*/
class Ad_Types extends Types {
/**
* Hook to filter types.
*
* @var string
*/
protected $hook = 'advanced-ads-ad-types';
/**
* Class for unknown type.
*
* @var string
*/
protected $type_unknown = Unknown::class;
/**
* Type interface to check.
*
* @var string
*/
protected $type_interface = Ad_Type::class;
/**
* Register default types.
*
* @return void
*/
protected function register_default_types(): void {
$this->register_type( Plain::class );
$this->register_type( Dummy::class );
$this->register_type( Content::class );
$this->register_type( Image::class );
$this->register_type( Group::class );
$this->register_type( GAM::class );
$this->register_type( AMP::class );
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* This class is responsible to hold all the Ads functionality.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads;
use AdvancedAds\Framework\Interfaces\Initializer_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Ads Ads.
*/
class Ads implements Initializer_Interface {
/**
* Hold factory instance
*
* @var Ad_Factory
*/
public $factory = null;
/**
* Hold repository instance
*
* @var Ad_Repository
*/
public $repository = null;
/**
* Hold types manager
*
* @var Ad_Types
*/
public $types = null;
/**
* Runs this initializer.
*
* @return void
*/
public function initialize(): void {
$this->factory = new Ad_Factory();
$this->types = new Ad_Types();
$this->repository = new Ad_Repository();
$this->types->hooks();
}
}

View File

@@ -0,0 +1,93 @@
<?php
/**
* This class represents the "AMP" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Dummy;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type AMP.
*/
class AMP implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'amp';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Dummy::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'AMP', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Ads that are visible on Accelerated Mobile Pages.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return true;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return 'https://wpadvancedads.com/add-ons/responsive-ads/';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/amp.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
}

View File

@@ -0,0 +1,161 @@
<?php
/**
* This class represents the "Content" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Content;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type Content.
*/
class Content implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'content';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Content::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'Rich Content', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'The full content editor from WordPress with all features like shortcodes, image upload or styling, but also simple text/html mode for scripts and code.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/content.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
/**
* Output for the ad parameters metabox
*
* @param Ad_Content $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
$content = $ad->get_content() ?? '';
/**
* Build the tinymc editor
*
* @link http://codex.wordpress.org/Function_Reference/wp_editor
*
* Don't build it when ajax is used; display message and buttons instead
*/
if ( wp_doing_ajax() ) :
// IMPORTANT: Keep textarea on a single line to prevent whitespace from being added to the content.
?>
<textarea id="advads-ad-content-plain" style="display:none;" cols="40" rows="10" name="advanced_ad[content]"><?php echo esc_textarea( $content ); ?></textarea>
<?php
else :
if ( ! user_can_richedit() ) {
$content = esc_textarea( $content );
}
add_filter( 'tiny_mce_before_init', [ $this, 'tiny_mce_before_init' ], 10, 2 );
$args = [
'textarea_name' => 'advanced_ad[content]',
'textarea_rows' => 10,
'drag_drop_upload' => true,
];
wp_editor( $content, 'advanced-ad-parameters-content', $args );
endif;
?>
<br class="clear"/>
<input type="hidden" name="advanced_ad[output][allow_shortcodes]" value="1" />
<?php
include ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-info-after-textarea.php';
}
/**
* Add JS into tinyMCE
*
* @param array $init_array TinyMCE arguments.
* @param string $editor_id Editor id.
*
* @return array
*/
public function tiny_mce_before_init( array $init_array, $editor_id ): array {
if ( 'advanced-ad-parameters-content' !== $editor_id ) {
return $init_array;
}
// Add a JS listener to trigger an `input` event for the rich text textarea.
$init_array['setup'] = <<<'JS'
[editor => {
const textarea = document.getElementById('advanced-ad-parameters-content');
editor.on('Dirty', event => {
textarea.value = editor.getContent();
textarea.dispatchEvent(new Event('input'));
});
}][0]
JS;
return $init_array;
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* This class represents the "Dummy" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Dummy;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type Dummy.
*/
class Dummy implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'dummy';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Dummy::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'Dummy', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Uses a simple placeholder ad for quick testing.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/dummy.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return false;
}
/**
* Output for the ad parameters metabox
*
* @param Ad_Dummy $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
if ( ! defined( 'AAT_VERSION' ) ) :
$url = $ad->get_url() ?? home_url();
?>
<span class="label"><?php esc_html_e( 'URL', 'advanced-ads' ); ?></span>
<div>
<input type="text" name="advanced_ad[url]" id="advads-url" class="advads-ad-url" value="<?php echo esc_url( $url ); ?>" />
</div>
<hr/>
<?php
endif;
?>
<img src="<?php echo esc_url( ADVADS_BASE_URL ) . 'public/assets/img/dummy.jpg'; ?>" alt="" width="300" height="250" />
<input type="hidden" name="advanced_ad[width]" value="300" />
<input type="hidden" name="advanced_ad[height]" value="250" />
<?php
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* This class represents the "GAM" ad type.
*
* @package AdvancedAds\GAM
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Dummy;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type GAM.
*/
class GAM implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'gam';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Dummy::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'Google Ad Manager', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Load ad units directly from your Google Ad Manager account.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return true;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return 'https://wpadvancedads.com/add-ons/google-ad-manager/';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/gam.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* This class represents the "Group" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Constants;
use AdvancedAds\Ads\Ad_Group;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type Group.
*/
class Group implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'group';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Group::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'Ad Group', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Choose an existing ad group. Use this type when you want to assign the same display and visitor conditions to all ads in that group.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/group.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
/**
* Output for the ad parameters metabox
*
* @param Ad_Group $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
?>
<label for="advads-group-id" class="label">
<?php esc_html_e( 'Ad Group', 'advanced-ads' ); ?>
</label>
<div>
<?php
wp_dropdown_categories(
[
'name' => 'advanced_ad[output][group_id]',
'id' => 'advads-group-id',
'selected' => $ad->get_group_id() ?? '',
'taxonomy' => Constants::TAXONOMY_GROUP,
'hide_empty' => false,
'show_option_none' => esc_html__( 'Select a group', 'advanced-ads' ),
]
);
?>
</div>
<hr/>
<?php
}
}

View File

@@ -0,0 +1,292 @@
<?php
/**
* This class represents the "Image" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Image;
use AdvancedAds\Interfaces\Ad_Type;
use AdvancedAds\Utilities\WordPress;
defined( 'ABSPATH' ) || exit;
/**
* Type Image.
*/
class Image implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'image';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Image::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'Image Ad', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Ads in various image formats.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/image.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
/**
* Render preview on the ad overview list
*
* @param Ad_Image $ad Ad instance.
*
* @TODO: refactor and test
*
* @return void
*/
public function render_preview( Ad_Image $ad ): void {
if ( empty( $ad->get_image_id() ) ) {
return;
}
list( $src, $width, $height ) = wp_get_attachment_image_src( $ad->get_image_id(), 'medium', true );
$preview_size_small = 50;
$preview_size_large = 200;
// Scale down width or height for the preview.
if ( $width > $height ) {
$preview_height = ceil( $height / ( $width / $preview_size_small ) );
$preview_width = $preview_size_small;
$tooltip_height = ceil( $height / ( $width / $preview_size_large ) );
$tooltip_width = $preview_size_large;
} else {
$preview_width = ceil( $width / ( $height / $preview_size_small ) );
$preview_height = $preview_size_small;
$tooltip_width = ceil( $width / ( $height / $preview_size_large ) );
$tooltip_height = $preview_size_large;
}
$preview_hwstring = image_hwstring( $preview_width, $preview_height );
$tooltip_hwstring = image_hwstring( $tooltip_width, $tooltip_height );
$alt = wp_strip_all_tags( get_post_meta( $ad->get_image_id(), '_wp_attachment_image_alt', true ) );
include ADVADS_ABSPATH . 'admin/views/ad-list/preview-image.php';
}
/**
* Output for the ad parameters metabox
*
* @param Ad_Image $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
$id = $ad->get_image_id() ?? '';
$url = $ad->get_url() ?? '';
$edit_link = $id ? get_edit_post_link( $id ) : '';
?><span class="label">
<button href="#" class="advads_image_upload button advads-button-secondary" type="button"
data-uploader-title="<?php esc_attr_e( 'Insert File', 'advanced-ads' ); ?>"
data-uploader-button-text="<?php esc_attr_e( 'Insert', 'advanced-ads' ); ?>"
onclick="return false;">
<?php esc_html_e( 'Select image', 'advanced-ads' ); ?>
</button>
</span>
<div>
<input type="hidden" name="advanced_ad[output][image_id]" value="<?php echo absint( $id ); ?>" id="advads-image-id"/>
<div id="advads-image-preview">
<?php $this->create_image_tag( $id, $ad ); ?>
</div>
<a id="advads-image-edit-link" class="<?php echo ! $edit_link ? 'hidden' : ''; ?>" href="<?php echo esc_url( $edit_link ); ?>"><span class="dashicons dashicons-edit"></span></a>
</div>
<hr/>
<?php
if ( ! defined( 'AAT_VERSION' ) ) :
?>
<label for="advads-url" class="label"><?php esc_html_e( 'URL', 'advanced-ads' ); ?></label>
<div>
<input type="url" name="advanced_ad[url]" id="advads-url" class="advads-ad-url" value="<?php echo esc_url( $url ); ?>" placeholder="https://www.example.com/"/>
<p class="description">
<?php esc_html_e( 'Link to target site including http(s)', 'advanced-ads' ); ?>
</p>
</div>
<hr/>
<?php
endif;
}
/**
* Generate a string with the original image size for output in the backend
* Only show, if different from entered image sizes
*
* @param Ad_Image $ad Ad instance.
*
* @return string empty, if the entered size is the same as the original size
*/
public function show_original_image_size( Ad_Image $ad ) {
$attachment_id = $ad->get_image_id() ?? '';
$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
if ( $attachment ) {
list( $src, $width, $height ) = $attachment;
?>
<p class="description">
<?php
if ( $ad->get_width() !== $width || $ad->get_height() !== $height ) :
printf(
/* translators: $s is a size string like "728 x 90". */
esc_attr__( 'Original size: %s', 'advanced-ads' ),
esc_html( $width ) . '&nbsp;x&nbsp;' . esc_html( $height )
);
?>
</p>
<?php
endif;
}
return '';
}
/**
* Render image tag
*
* @param int $attachment_id Attachment id.
* @param Ad_Image $ad Ad instance.
*
* @return void
*/
public function create_image_tag( $attachment_id, $ad ): void {
global $wp_current_filter;
$style = '';
$image = wp_get_attachment_image_src( $attachment_id, 'full' );
// Early bail!!
if ( ! $image ) {
return;
}
list( $src, $width, $height ) = $image;
// Override image size with the size given in ad options, but in frontend only.
if ( ! is_admin() || wp_doing_ajax() ) {
$width = $ad->get_width();
$height = $ad->get_height();
}
$hwstring = image_hwstring( $width, $height );
$alt = trim( esc_textarea( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) );
// TODO: use an array for attributes so they are simpler to extend.
$sizes = '';
$srcset = '';
$more_attributes = $srcset;
// Create srcset and sizes attributes if we are in the the_content filter and in WordPress 4.4.
if (
isset( $wp_current_filter )
&& in_array( 'the_content', $wp_current_filter, true )
&& ! defined( 'ADVADS_DISABLE_RESPONSIVE_IMAGES' )
) {
if ( function_exists( 'wp_get_attachment_image_srcset' ) ) {
$srcset = wp_get_attachment_image_srcset( $attachment_id, 'full' );
}
if ( function_exists( 'wp_get_attachment_image_sizes' ) ) {
$sizes = wp_get_attachment_image_sizes( $attachment_id, 'full' );
}
if ( $srcset && $sizes ) {
$more_attributes .= ' srcset="' . $srcset . '" sizes="' . $sizes . '"';
}
}
// TODO: move to classes/compabtility.php when we have a simpler filter for additional attributes
// Compabitility with WP Smush.
// Disables their lazy load for image ads because it caused them to not show up in certain positions at all.
$wp_smush_settings = get_option( 'wp-smush-settings' );
if ( isset( $wp_smush_settings['lazy_load'] ) && $wp_smush_settings['lazy_load'] ) {
$more_attributes .= ' class="no-lazyload"';
}
// Add css rule to be able to center the ad.
if ( strpos( $ad->get_position(), 'center' ) === 0 ) {
$style .= 'display: inline-block;';
}
$style = apply_filters( 'advanced-ads-ad-image-tag-style', $style );
$style = '' !== $style ? 'style="' . $style . '"' : '';
$more_attributes = apply_filters( 'advanced-ads-ad-image-tag-attributes', $more_attributes );
$more_attributes .= ' ' . $hwstring . ' ' . $style;
$img = sprintf( '<img src="%s" alt="%s" %s />', esc_url( $src ), esc_attr( $alt ), $more_attributes );
// Add 'loading' attribute if applicable, available from WP 5.5.
if (
$wp_current_filter
&& function_exists( 'wp_lazy_loading_enabled' )
&& wp_lazy_loading_enabled( 'img', current_filter() )
&& ! strpos( $more_attributes, 'loading=' )
) {
// Optimize image HTML tag with loading attributes based on WordPress filter context.
$img = WordPress::img_tag_add_loading_attr( $img, current_filter() );
}
echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}

View File

@@ -0,0 +1,235 @@
<?php
/**
* This class represents the "Plain" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Plain;
use AdvancedAds\Interfaces\Ad_Type;
use AdvancedAds\Utilities\Conditional;
defined( 'ABSPATH' ) || exit;
/**
* Type Plain.
*/
class Plain implements Ad_Type {
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return 'plain';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return Ad_Plain::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return __( 'Plain Text and Code', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return __( 'Any ad network, Amazon, customized AdSense codes, shortcodes, and code like JavaScript, HTML or PHP.', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return false;
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
return ADVADS_BASE_URL . 'assets/img/ad-types/plain.svg';
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
/**
* Output for the ad parameters metabox
*
* @param Ad_Plain $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
$content = $ad->get_content() ?? '';
?>
<p class="description">
<?php esc_html_e( 'Insert plain text or code into this field.', 'advanced-ads' ); ?>
</p>
<?php $this->error_unfiltered_html( $ad ); ?>
<textarea
id="advads-ad-content-plain"
cols="40"
rows="10"
name="advanced_ad[content]"
><?php echo esc_textarea( $content ); ?></textarea>
<?php
include ADVADS_ABSPATH . 'views/admin/metaboxes/ads/ad-info-after-textarea.php';
$this->render_php_allow( $ad );
$this->render_shortcodes_allow( $ad );
?>
<?php
}
/**
* Render php output field
*
* @param Ad_Plain $ad Ad instance.
*
* @return void
*/
private function render_php_allow( $ad ) {
?>
<label class="label" for="advads-parameters-php">
<?php esc_html_e( 'Allow PHP', 'advanced-ads' ); ?>
</label>
<div>
<input type="hidden" name="advanced_ad[allow_php]" value="off">
<input id="advads-parameters-php" type="checkbox" name="advanced_ad[allow_php]" value="on"<?php checked( $ad->is_php_allowed() ); ?><?php disabled( ! Conditional::is_php_allowed() ); ?> />
<span class="advads-help">
<span class="advads-tooltip">
<?php
echo wp_kses(
__( 'Execute PHP code (wrapped in <code>&lt;?php ?&gt;</code>)', 'advanced-ads' ),
[
'code' => [],
]
);
?>
</span>
</span>
<?php if ( ! Conditional::is_php_allowed() ) : ?>
<p class="advads-notice-inline advads-error">
<?php
printf(
/* translators: The name of the constant preventing PHP execution */
esc_html__( 'Executing PHP code has been disallowed by %s', 'advanced-ads' ),
sprintf( '<code>%s</code>', defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ? 'DISALLOW_FILE_EDIT' : 'ADVANCED_ADS_DISALLOW_PHP' )
);
?>
</p>
<?php else : ?>
<p class="advads-notice-inline advads-error" id="advads-allow-php-warning" style="display:none;">
<?php esc_html_e( 'Using PHP code can be dangerous. Please make sure you know what you are doing.', 'advanced-ads' ); ?>
</p>
<?php endif; ?>
<p class="advads-notice-inline advads-error" id="advads-parameters-php-warning" style="display:none;">
<?php esc_html_e( 'No PHP tag detected in your code.', 'advanced-ads' ); ?> <?php esc_html_e( 'Uncheck this checkbox for improved performance.', 'advanced-ads' ); ?>
</p>
</div>
<hr/>
<?php
}
/**
* Render allow shortcodes field.
*
* @param Ad_Plain $ad Ad instance.
*
* @return void
*/
private function render_shortcodes_allow( $ad ): void {
$allow_shortcodes = absint( $ad->is_shortcode_allowed() );
?>
<label class="label"
for="advads-parameters-shortcodes"><?php esc_html_e( 'Execute shortcodes', 'advanced-ads' ); ?></label>
<div>
<input type="hidden" name="advanced_ad[output][allow_shortcodes]" value="off"/>
<input id="advads-parameters-shortcodes" type="checkbox" name="advanced_ad[output][allow_shortcodes]" value="on" <?php checked( $allow_shortcodes ); ?>/>
<p class="advads-notice-inline advads-error" id="advads-parameters-shortcodes-warning"
style="display:none;"><?php esc_html_e( 'No shortcode detected in your code.', 'advanced-ads' ); ?> <?php esc_html_e( 'Uncheck this checkbox for improved performance.', 'advanced-ads' ); ?></p>
</div>
<hr/>
<?php
}
/**
* Check if we're on an ad edit screen, if yes and the user does not have `unfiltered_html` permissions,
* show an admin notice.
*
* @param Ad_Plain $ad Ad instance.
*
* @return void
*/
private function error_unfiltered_html( $ad ): void {
$author_id = absint( get_post_field( 'post_author', $ad->get_id() ) );
$user = wp_get_current_user();
$current_user_id = $user->ID;
if ( Conditional::can_author_unfiltered_html( $author_id ) ) {
return;
}
?>
<p class="advads-notice-inline advads-error">
<?php
if ( $author_id === $current_user_id ) {
esc_html_e( 'You do not have sufficient permissions to include all HTML tags.', 'advanced-ads' );
} else {
esc_html_e( 'The creator of the ad does not have sufficient permissions to include all HTML tags.', 'advanced-ads' );
if (
current_user_can( 'unfiltered_html' )
&& Conditional::has_user_role_on_site()
&& ! empty( $user->caps['administrator'] ) // A superadmin won't be listed in the author dropdown if he's registered as something other than admin on a blog of the network.
) {
printf( '<button type="button" onclick="(()=>Advanced_Ads_Admin.reassign_ad(%d))();" class="button button-primary">%s</button>', esc_attr( $current_user_id ), esc_html__( 'Assign ad to me', 'advanced-ads' ) );
}
}
?>
<a href="https://wpadvancedads.com/manual/ad-types/#Plain_Text_and_Code" class="advads-manual-link" target="_blank" rel="noopener">
<?php esc_html_e( 'Manual', 'advanced-ads' ); ?>
</a>
</p>
<?php
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* This class represents the "Unknown" ad type.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.0
*/
namespace AdvancedAds\Ads\Types;
use AdvancedAds\Ads\Ad_Dummy;
use AdvancedAds\Interfaces\Ad_Type;
defined( 'ABSPATH' ) || exit;
/**
* Type Unknown.
*/
class Unknown implements Ad_Type {
/**
* Hold type data.
*
* @var array
*/
private $data = [];
/**
* The constructor.
*
* @param array $data Array of type data.
*/
public function __construct( array $data ) {
$this->data = $data;
}
/**
* Get the unique identifier (ID) of the ad type.
*
* @return string The unique ID of the ad type.
*/
public function get_id(): string {
return $this->data['id'] ?? 'default';
}
/**
* Get the class name of the object as a string.
*
* @return string
*/
public function get_classname(): string {
return $this->data['classname'] ?? Ad_Dummy::class;
}
/**
* Get the title or name of the ad type.
*
* @return string The title of the ad type.
*/
public function get_title(): string {
return $this->data['title'] ?? __( 'Unknown type', 'advanced-ads' );
}
/**
* Get a description of the ad type.
*
* @return string The description of the ad type.
*/
public function get_description(): string {
return $this->data['description'] ?? __( 'No description', 'advanced-ads' );
}
/**
* Check if this ad type requires premium.
*
* @return bool True if premium is required; otherwise, false.
*/
public function is_premium(): bool {
return boolval( $this->data['is_upgrade'] ?? $this->data['is_premium'] ?? true );
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_upgrade_url(): string {
return $this->data['upgrade_url'] ?? '';
}
/**
* Get the URL for upgrading to this ad type.
*
* @return string The upgrade URL for the ad type.
*/
public function get_image(): string {
if ( isset( $this->data['icon'] ) && ! empty( $this->data['icon'] ) ) {
return $this->data['icon'];
}
$icon_path = sprintf( 'assets/img/ad-types/%s.svg', $this->get_id() );
if ( ! file_exists( ADVADS_ABSPATH . $icon_path ) ) {
$icon_path = 'assets/img/ad-types/empty.svg';
}
return ADVADS_BASE_URL . $icon_path;
}
/**
* Check if this ad type has size parameters.
*
* @return bool True if has size parameters; otherwise, false.
*/
public function has_size(): bool {
return true;
}
/**
* Output for the ad parameters metabox
*
* @param Ad $ad Ad instance.
*
* @return void
*/
public function render_parameters( $ad ): void {
if ( isset( $this->data['render_parameters'] ) && is_callable( $this->data['render_parameters'] ) ) {
$this->data['render_parameters']( $ad );
}
}
}

View File

@@ -0,0 +1,95 @@
<?php // phpcs:ignoreFile
/**
* Conditions under which to (not) show an ad
* I dont like huge arrays like this to clutter my classes
* and anyway, this might be needed on multiple places
* at the bottom, you find a filter to be able to extend / remove your own elements
*
* Elements
* key - internal id of the condition; needs to be unique, obviously
* label - title in the dashboard
* description - (optional) description displayed in the dashboard
* type - information / markup type
* idfield - input field for comma separated lists of ids
* radio - radio button
* others - added to not trigger internal sanitization
*
* note: idfield always has a {field}_not version that is created automatically and being its own condition
*
* @deprecated since 1.7
* @package AdvancedAds
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'Advanced_Ads', false ) ) {
die();
}
$advanced_ads_ad_conditions = [
'enabled' => [
'type' => 'other',
],
'posttypes' => [
'label' => __( 'Post Types', 'advanced-ads' ),
'description' => __( 'Choose the public post types on which to display the ad.', 'advanced-ads' ),
'type' => 'textvalues',
'callback' => [ 'Advanced_Ads_Display_Condition_Callbacks', 'post_types']
],
'categoryids' => [
'label' => __( 'Categories, Tags and Taxonomies', 'advanced-ads' ),
'description' => __( 'Choose terms from public category, tag and other taxonomies a post must belong to in order to have ads.', 'advanced-ads' ),
'type' => 'idfield',
'callback' => [ 'Advanced_Ads_Display_Condition_Callbacks', 'terms']
],
'categoryarchiveids' => [
'label' => __( 'Category Archives', 'advanced-ads' ),
'description' => __( 'comma seperated IDs of category archives', 'advanced-ads' ),
'type' => 'idfield',
'callback' => [ 'Advanced_Ads_Display_Condition_Callbacks', 'category_archives']
],
'postids' => [
'label' => __( 'Individual Posts, Pages and Public Post Types', 'advanced-ads' ),
'description' => __( 'Choose on which individual posts, pages and public post type pages you want to display or hide ads.', 'advanced-ads' ),
'type' => 'other',
'callback' => [ 'Advanced_Ads_Display_Condition_Callbacks', 'single_posts']
],
'is_front_page' => [
'label' => __( 'Home Page', 'advanced-ads' ),
'description' => __( 'show on Home page', 'advanced-ads' ),
'type' => 'radio',
],
'is_singular' => [
'label' => __( 'Singular Pages', 'advanced-ads' ),
'description' => __( 'show on singular pages/posts', 'advanced-ads' ),
'type' => 'radio',
],
'is_archive' => [
'label' => __( 'Archive Pages', 'advanced-ads' ),
'description' => __( 'show on any type of archive page (category, tag, author and date)', 'advanced-ads' ),
'type' => 'radio',
],
'is_search' => [
'label' => __( 'Search Results', 'advanced-ads' ),
'description' => __( 'show on search result pages', 'advanced-ads' ),
'type' => 'radio',
],
'is_404' => [
'label' => __( '404 Page', 'advanced-ads' ),
'description' => __( 'show on 404 error page', 'advanced-ads' ),
'type' => 'radio',
],
'is_attachment' => [
'label' => __( 'Attachment Pages', 'advanced-ads' ),
'description' => __( 'show on attachment pages', 'advanced-ads' ),
'type' => 'radio',
],
'is_main_query' => [
'label' => __( 'Secondary Queries', 'advanced-ads' ),
'description' => __( 'allow ads in secondary queries', 'advanced-ads' ),
'type' => 'radio',
],
];
return apply_filters( 'advanced-ads-conditions', $advanced_ads_ad_conditions );

View File

@@ -0,0 +1,15 @@
<?php
/**
* Advanced Ads capabilities
*
* currently only for informational purposes
*/
$advanced_ads_capabilities = apply_filters( 'advanced-ads-capabilities', [
'advanced_ads_manage_options', // admins only
'advanced_ads_see_interface', // admins, maybe editors
'advanced_ads_edit_ads', // admins, maybe editors
'advanced_ads_manage_placements', // admins, maybe editors
'advanced_ads_place_ads', // admins, maybe editors
]);

View File

@@ -0,0 +1,123 @@
<?php
/**
* Assets registry handles the registration of stylesheets and scripts required for plugin functionality.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds;
use AdvancedAds\Framework;
defined( 'ABSPATH' ) || exit;
/**
* Assets Registry.
*/
class Assets_Registry extends Framework\Assets_Registry {
/**
* Version for plugin local assets.
*
* @return string
*/
public function get_version(): string {
return ADVADS_VERSION;
}
/**
* Prefix to use in handle to make it unique.
*
* @return string
*/
public function get_prefix(): string {
return ADVADS_SLUG;
}
/**
* Base URL for plugin local assets.
*
* @return string
*/
public function get_base_url(): string {
return ADVADS_BASE_URL;
}
/**
* Register styles
*
* @return void
*/
public function register_styles(): void {
$this->register_style( 'ui', 'admin/assets/css/ui.css' );
$this->register_style( 'admin', 'admin/assets/css/admin.css' );
$this->register_style( 'ad-positioning', 'modules/ad-positioning/assets/css/ad-positioning.css', [ self::prefix_it( 'admin' ) ] );
// New CSS files.
$this->register_style( 'common', 'assets/css/admin/common.css' );
$this->register_style( 'notifications', 'assets/css/admin/notifications.css' );
$this->register_style( 'screen-ads-editing', 'assets/css/admin/screen-ads-editing.css', [ self::prefix_it( 'common' ) ] );
$this->register_style( 'screen-ads-listing', 'assets/css/admin/screen-ads-listing.css', [] );
$this->register_style( 'screen-dashboard', 'assets/css/admin/screen-dashboard.css', [ self::prefix_it( 'common' ), 'wp-components' ] );
$this->register_style( 'screen-groups-listing', 'assets/css/admin/screen-groups-listing.css' );
$this->register_style( 'screen-onboarding', 'assets/css/admin/screen-onboarding.css' );
$this->register_style( 'screen-placements-listing', 'assets/css/admin/screen-placements-listing.css' );
$this->register_style( 'screen-settings', 'assets/css/admin/screen-settings.css', [ self::prefix_it( 'common' ) ] );
$this->register_style( 'screen-tools', 'assets/css/admin/screen-status.css', [ self::prefix_it( 'common' ) ] );
$this->register_style( 'wp-dashboard', 'assets/css/admin/wp-dashboard.css', [ self::prefix_it( 'common' ) ] );
}
/**
* Register scripts
*
* @return void
*/
public function register_scripts(): void {
$this->register_script( 'admin-global', 'admin/assets/js/admin-global.js', [ 'jquery' ], false, true );
$this->register_script( 'find-adblocker', 'admin/assets/js/advertisement.js' );
$this->register_script( 'ui', 'admin/assets/js/ui.js', [ 'jquery' ] );
$this->register_script( 'conditions', 'admin/assets/js/conditions.js', [ 'jquery', self::prefix_it( 'ui' ) ] );
$this->register_script( 'wizard', 'admin/assets/js/wizard.js', [ 'jquery' ] );
$this->register_script( 'inline-edit-group-ads', 'admin/assets/js/inline-edit-group-ads.js', [ 'jquery' ], false, false );
$this->register_script( 'ad-positioning', '/modules/ad-positioning/assets/js/ad-positioning.js', [], false, true );
$this->register_script( 'admin', 'admin/assets/js/admin.min.js', [ 'jquery', self::prefix_it( 'ui' ), 'jquery-ui-autocomplete', 'wp-util' ], false, false );
$this->register_script( 'groups', 'admin/assets/js/groups.js', [ 'jquery' ], false, true );
$this->register_script( 'adblocker-image-data', 'admin/assets/js/adblocker-image-data.js', [ 'jquery' ] );
// New JS files.
$this->register_script( 'admin-common', 'assets/js/admin/admin-common.js', [ 'jquery' ], false, true );
$this->register_script( 'screen-ads-listing', 'assets/js/admin/screen-ads-listing.js', [ 'jquery', 'inline-edit-post', 'wp-util', 'wp-api-fetch', self::prefix_it( 'admin-common' ) ], false, true );
$this->register_script( 'screen-ads-editing', 'assets/js/admin/screen-ads-editing.js', [], false, true );
$this->register_script( 'screen-dashboard', 'assets/js/admin/screen-dashboard.js', [ self::prefix_it( 'admin-common' ) ], false, true );
$this->register_script( 'screen-groups-listing', 'assets/js/admin/screen-groups-listing.js', [ 'wp-api-fetch', self::prefix_it( 'admin-common' ) ], false, true );
$this->register_script( 'screen-placements-listing', 'assets/js/admin/screen-placements-listing.js', [ 'wp-util', 'wp-api-fetch', self::prefix_it( 'admin-global' ), self::prefix_it( 'admin-common' ) ], false, true );
$this->register_script( 'screen-settings', 'assets/js/admin/screen-settings.js', [], false, true );
$this->register_script( 'screen-tools', 'assets/js/admin/screen-tools.js', [], false, true );
$this->register_script( 'wp-dashboard', 'assets/js/admin/wp-dashboard.js', [ 'jquery' ], false, true );
$this->register_script( 'notifications-center', 'assets/js/admin/notifications.js', [ 'jquery' ], false, true );
$onboarding_deps = [
'jquery',
'lodash',
'moment',
'wp-data',
'wp-compose',
'wp-components',
'wp-api-fetch',
];
$this->register_script( 'screen-onboarding', 'assets/js/screen-onboarding.js', $onboarding_deps, false, true );
$this->register_script( 'page-quick-edit', 'assets/js/admin/page-quick-edit.js', [ 'wp-api-fetch' ], false, true );
// OneClick.
$deps = [
'jquery',
'wp-dom-ready',
'wp-components',
'wp-notices',
'wp-element',
'wp-html-entities',
];
$this->register_script( 'oneclick-onboarding', 'assets/js/admin/oneclick-onboarding.js', $deps, false, true );
}
}

View File

@@ -0,0 +1,213 @@
<?php
/**
* The class is responsible for locating and loading the autoloader file used in the plugin.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds;
defined( 'ABSPATH' ) || exit;
/**
* Autoloader.
*/
class Autoloader {
/**
* Hold autoloader.
*
* @var mixed
*/
private $autoloader;
/**
* Main instance
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Autoloader
*/
public static function get(): Autoloader {
static $instance;
if ( null === $instance ) {
$instance = new Autoloader();
}
return $instance;
}
/**
* Get hold autoloader.
*
* @return mixed
*/
public function get_autoloader() {
return $this->autoloader;
}
/**
* Get plugin directory.
*
* @return string
*/
public function get_directory(): string {
return dirname( ADVADS_FILE );
}
/**
* Runs this initializer.
*
* @return void
*/
public function initialize(): void {
$locate = $this->locate();
if ( ! $locate ) {
add_action( 'admin_notices', [ $this, 'missing_autoloader' ] );
return;
}
$this->autoloader = require $locate;
$this->register_wordpress();
}
/**
* Locate the autoload file
*
* This function searches for the autoload file in the packages directory and vendor directory.
*
* @return bool|string
*/
private function locate() {
$directory = $this->get_directory();
$packages = $directory . '/packages/autoload.php';
$vendors = $directory . '/vendor/autoload.php';
$is_debug = $this->is_debug() || 'local' === $this->get_environment_type();
if ( is_readable( $packages ) && ( ! $is_debug || ! is_readable( $vendors ) ) ) {
return $packages;
}
if ( is_readable( $vendors ) ) {
return $vendors;
}
return false;
}
/**
* Add WordPress classes to map
*
* @return void
*/
private function register_wordpress(): void {
$this->autoloader->addClassmap(
[
'WP_List_Table' => ABSPATH . 'wp-admin/includes/class-wp-list-table.php',
'WP_Terms_List_Table' => ABSPATH . 'wp-admin/includes/class-wp-terms-list-table.php',
]
);
}
/**
* If the autoloader is missing, add an admin notice.
*
* @return void
*/
protected function missing_autoloader(): void {
?>
<div class="notice notice-error">
<p>
<?php
printf(
/* translators: 1: is a link to a support document. 2: closing link */
esc_html__( 'Your installation of Advanced Ads is incomplete. If you installed Advanced Ads from GitHub, %1$s please refer to this document%2$s to set up your development environment.', 'advanced-ads' ),
'<a href="' . esc_url( 'https://github.com/advanced-ads/advanced-ads/wiki/How-to-set-up-development-environment' ) . '" target="_blank" rel="noopener noreferrer">',
'</a>'
);
?>
</p>
</div>
<?php
}
/**
* Retrieves the current environment type.
*
* @return string
*/
public function get_environment_type(): string {
return function_exists( 'wp_get_environment_type' )
? wp_get_environment_type()
: $this->get_environment_type_fallback();
}
/**
* Retrieves the current environment type.
*
* @return string
*/
private function get_environment_type_fallback(): string {
static $current_env = '';
if ( ! defined( 'WP_RUN_CORE_TESTS' ) && $current_env ) {
return $current_env;
}
$wp_environments = [
'local',
'development',
'staging',
'production',
];
// Add a note about the deprecated WP_ENVIRONMENT_TYPES constant.
if ( defined( 'WP_ENVIRONMENT_TYPES' ) && function_exists( '_deprecated_argument' ) ) {
if ( function_exists( '__' ) ) {
/* translators: %s: WP_ENVIRONMENT_TYPES */
$message = sprintf( __( 'The %s constant is no longer supported.', 'advanced-ads' ), 'WP_ENVIRONMENT_TYPES' );
} else {
$message = sprintf( 'The %s constant is no longer supported.', 'WP_ENVIRONMENT_TYPES' );
}
_deprecated_argument(
'define()',
'5.5.1',
$message // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
// Check if the environment variable has been set, if `getenv` is available on the system.
if ( function_exists( 'getenv' ) ) {
$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
if ( false !== $has_env ) {
$current_env = $has_env;
}
}
// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
$current_env = WP_ENVIRONMENT_TYPE;
}
// Make sure the environment is an allowed one, and not accidentally set to an invalid value.
if ( ! in_array( $current_env, $wp_environments, true ) ) {
$current_env = 'production';
}
return $current_env;
}
/**
* Is WordPress debug mode enabled
*
* @return bool
*/
private function is_debug(): bool {
return defined( 'WP_DEBUG' ) && WP_DEBUG;
}
}

View File

@@ -0,0 +1,200 @@
<?php
/**
* Constants.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds;
defined( 'ABSPATH' ) || exit;
/**
* Constants.
*/
class Constants {
/**
* Rest API base.
*
* @var string
*/
const REST_BASE = 'advanced-ads/v1';
/**
* Prefix of selectors (id, class) in the frontend
* can be changed by options
*
* @var string
*/
const DEFAULT_FRONTEND_PREFIX = 'advads-';
/**
* Constant representing the slug for the hidden page in the Advanced Ads plugin.
*
* @var string HIDDEN_PAGE_SLUG The slug for the hidden page.
*/
const HIDDEN_PAGE_SLUG = 'advanced_ads_hidden_page_slug';
/* Entity Types ------------------- */
/**
* The ad entity type.
*
* @var string
*/
const ENTITY_AD = 'ad';
/**
* The group entity type.
*
* @var string
*/
const ENTITY_GROUP = 'group';
/**
* The placement entity type.
*
* @var string
*/
const ENTITY_PLACEMENT = 'placement';
/* Post Types and Taxonomies Slugs ------------------- */
/**
* The ad post type slug.
*
* @var string
*/
const POST_TYPE_AD = 'advanced_ads';
/**
* The placement post type slug.
*
* @var string
*/
const POST_TYPE_PLACEMENT = 'advanced_ads_plcmnt';
/**
* The group taxonomy slug.
*
* @var string
*/
const TAXONOMY_GROUP = 'advanced_ads_groups';
/* Post Types Status ------------------- */
/**
* Ad post expired status
*
* @var string
*/
const AD_STATUS_EXPIRED = 'advanced_ads_expired';
/**
* Ad post expiring status
*
* @var string
*/
const AD_STATUS_EXPIRING = 'advanced_ads_expiring';
/* Cron Jobs Hooks ------------------- */
/**
* Ad expiration cron job hook.
*
* @var string
*/
const CRON_JOB_AD_EXPIRATION = 'advanced-ads-ad-expiration';
/* Meta keys ------------------- */
/**
* Ad metakey for expiry time.
*
* @var string
*/
const AD_META_EXPIRATION_TIME = 'advanced_ads_expiration_time';
/**
* Ad metakey for group ids.
*
* @var string
*/
const AD_META_GROUP_IDS = 'advanced_ads_ad_group_ids';
/* User Meta Keys ------------------- */
/**
* Wizard notice dismiss.
*
* @var string
*/
const USER_WIZARD_DISMISS = 'advanced-ads-notice-wizard-dismiss';
/* Option keys ------------------- */
/**
* Option key for the completion status of the wizard.
*
* @var string
*/
const OPTION_WIZARD_COMPLETED = '_advanced_ads_wizard_completed';
/**
* Option key for adblocker settings.
*
* @var string
*/
const OPTION_ADBLOCKER_SETTINGS = 'advanced-ads-adblocker';
/* Entity: Group ------------------- */
/**
* Default ad group weight
*/
const GROUP_AD_DEFAULT_WEIGHT = 10;
/* Misc ---------------------------- */
const ADDONS_NON_COMPATIBLE_VERSIONS = [
'1.1.3' => 'advanced-ads-adsense-in-feed', // Advanced Ads Google AdSense In-feed Placement.
'2.5.0' => 'advanced-ads-gam', // GAM.
'1.0.8' => 'advanced-ads-genesis', // Genesis.
'1.3.5' => 'advanced-ads-geo', // Geo.
'1.7.9' => 'advanced-ads-layer', // Layer Ads.
'0.1.3' => 'advanced-ads-page-peel', // Page Peel.
'2.28.0' => 'advanced-ads-pro', // Pro.
'1.12.3' => 'advanced-ads-responsive', // AMP former Responsive Ads.
'1.4.5' => 'advanced-ads-selling', // Selling.
'1.4.10' => 'slider-ads', // Slider.
'1.8.6' => 'advanced-ads-sticky', // Sticky Ads.
'2.8.1' => 'advanced-ads-tracking', // Tracking.
'1.0.7' => 'ads-for-visual-composer', // Visual Composer.
'1.1.0' => 'advanced-ads-browser-language', // Browser Language.
];
/**
* License API endpoint URL
*
* @const string
*/
const API_ENDPOINT = 'https://wpadvancedads.com/license-api/';
/**
* Add-on slugs and their EDD ID
*
* @const array
*/
const ADDON_SLUGS_ID = [
'advanced-ads-gam' => 215545,
'advanced-ads-layer' => 686,
'advanced-ads-pro' => 1742,
'advanced-ads-responsive' => 678,
'advanced-ads-selling' => 35300,
'advanced-ads-sticky' => 683,
'advanced-ads-tracking' => 638,
'slider-ads' => 1168,
];
}

View File

@@ -0,0 +1,247 @@
<?php
/**
* The class handles the registration of custom post types and taxonomies in the plugin.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Entities.
*/
class Entities implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
$this->register_ad_post_type();
$this->register_placement_post_type();
$this->register_group_taxonomy();
}
/**
* Register ad post type.
*
* @return void
*/
private function register_ad_post_type(): void {
// Early bail!!
if ( post_type_exists( Constants::POST_TYPE_AD ) ) {
return;
}
$labels = [
'name' => __( 'Ads', 'advanced-ads' ),
'singular_name' => __( 'Ad', 'advanced-ads' ),
'add_new' => __( 'New Ad', 'advanced-ads' ),
'add_new_item' => __( 'Add New Ad', 'advanced-ads' ),
'edit' => __( 'Edit', 'advanced-ads' ),
'edit_item' => __( 'Edit Ad', 'advanced-ads' ),
'new_item' => __( 'New Ad', 'advanced-ads' ),
'view' => __( 'View', 'advanced-ads' ),
'view_item' => __( 'View the Ad', 'advanced-ads' ),
'search_items' => __( 'Search Ads', 'advanced-ads' ),
'not_found' => __( 'No Ads found', 'advanced-ads' ),
'not_found_in_trash' => __( 'No Ads found in Trash', 'advanced-ads' ),
'parent' => __( 'Parent Ad', 'advanced-ads' ),
];
$supports = [ 'title', 'author' ];
if ( defined( 'ADVANCED_ADS_ENABLE_REVISIONS' ) ) {
$supports[] = 'revisions';
}
$args = [
'labels' => $labels,
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'hierarchical' => false,
'capabilities' => [
// Meta capabilities.
'edit_post' => 'advanced_ads_edit_ads',
'read_post' => 'advanced_ads_edit_ads',
'delete_post' => 'advanced_ads_edit_ads',
'edit_page' => 'advanced_ads_edit_ads',
'read_page' => 'advanced_ads_edit_ads',
'delete_page' => 'advanced_ads_edit_ads',
// Primitive capabilities used outside of map_meta_cap().
'edit_posts' => 'advanced_ads_edit_ads',
'publish_posts' => 'advanced_ads_edit_ads',
'read_private_posts' => 'advanced_ads_edit_ads',
// Primitive capabilities used within map_meta_cap().
'read' => 'advanced_ads_edit_ads',
'delete_posts' => 'advanced_ads_edit_ads',
'delete_private_posts' => 'advanced_ads_edit_ads',
'delete_published_posts' => 'advanced_ads_edit_ads',
'edit_private_posts' => 'advanced_ads_edit_ads',
'edit_published_posts' => 'advanced_ads_edit_ads',
'create_posts' => 'advanced_ads_edit_ads',
],
'has_archive' => false,
'query_var' => false,
'rewrite' => false,
'supports' => $supports,
'taxonomies' => [ Constants::TAXONOMY_GROUP ],
];
register_post_type(
Constants::POST_TYPE_AD,
apply_filters( 'advanced-ads-post-type-ad', $args )
);
register_post_status(
Constants::AD_STATUS_EXPIRED,
[
'label' => __( 'Expired', 'advanced-ads' ),
'private' => true,
]
);
}
/**
* Register placement post type.
*
* @return void
*/
private function register_placement_post_type(): void {
// Early bail!!
if ( post_type_exists( Constants::POST_TYPE_PLACEMENT ) ) {
return;
}
$labels = [
'name' => __( 'Ad Placements', 'advanced-ads' ),
'singular_name' => __( 'Ad Placement', 'advanced-ads' ),
'add_new' => __( 'New Placement', 'advanced-ads' ),
'add_new_item' => __( 'Add New Placement', 'advanced-ads' ),
'edit' => __( 'Edit', 'advanced-ads' ),
'edit_item' => __( 'Edit Placement', 'advanced-ads' ),
'new_item' => __( 'New Placement', 'advanced-ads' ),
'view' => __( 'View', 'advanced-ads' ),
'view_item' => __( 'View the Ad Placement', 'advanced-ads' ),
'search_items' => __( 'Search Ad Placements', 'advanced-ads' ),
'not_found' => __( 'No Ad Placements found', 'advanced-ads' ),
'not_found_in_trash' => __( 'No Ad Placements found in Trash', 'advanced-ads' ),
];
$args = [
'labels' => $labels,
'description' => __( 'Placements are physically places in your theme and posts. You can use them if you plan to change ads and ad groups on the same place without the need to change your templates.', 'advanced-ads' ),
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'hierarchical' => false,
'capabilities' => [
// Meta capabilities.
'edit_post' => 'advanced_ads_manage_placements',
'read_post' => 'advanced_ads_manage_placements',
'delete_post' => 'advanced_ads_manage_placements',
'edit_page' => 'advanced_ads_manage_placements',
'read_page' => 'advanced_ads_manage_placements',
'delete_page' => 'advanced_ads_manage_placements',
// Primitive capabilities used outside of map_meta_cap().
'edit_posts' => 'advanced_ads_manage_placements',
'publish_posts' => 'advanced_ads_manage_placements',
'read_private_posts' => 'advanced_ads_manage_placements',
// Primitive capabilities used within map_meta_cap().
'read' => 'advanced_ads_manage_placements',
'delete_posts' => 'advanced_ads_manage_placements',
'delete_private_posts' => 'advanced_ads_manage_placements',
'delete_published_posts' => 'advanced_ads_manage_placements',
'edit_private_posts' => 'advanced_ads_manage_placements',
'edit_published_posts' => 'advanced_ads_manage_placements',
'create_posts' => 'advanced_ads_manage_placements',
],
'has_archive' => false,
'query_var' => false,
'rewrite' => false,
'supports' => [ 'title' ],
];
register_post_type(
Constants::POST_TYPE_PLACEMENT,
apply_filters( 'advanced-ads-post-type-placement', $args )
);
}
/**
* Register group taxonomy.
*
* @return void
*/
private function register_group_taxonomy(): void {
// Early bail!!
if ( taxonomy_exists( Constants::TAXONOMY_GROUP ) ) {
return;
}
$labels = [
'name' => _x( 'Ad Groups & Rotations', 'ad group general name', 'advanced-ads' ),
'singular_name' => _x( 'Ad Group', 'ad group singular name', 'advanced-ads' ),
'search_items' => __( 'Search Ad Groups', 'advanced-ads' ),
'all_items' => __( 'All Ad Groups', 'advanced-ads' ),
'parent_item' => __( 'Parent Ad Groups', 'advanced-ads' ),
'parent_item_colon' => __( 'Parent Ad Groups:', 'advanced-ads' ),
'edit_item' => __( 'Edit Ad Group', 'advanced-ads' ),
'update_item' => __( 'Update Ad Group', 'advanced-ads' ),
'add_new_item' => __( 'New Ad Group', 'advanced-ads' ),
'new_item_name' => __( 'New Ad Groups Name', 'advanced-ads' ),
'menu_name' => __( 'Groups', 'advanced-ads' ),
'not_found' => __( 'No Ad Group found', 'advanced-ads' ),
];
$args = [
'public' => false,
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_in_nav_menus' => false,
'show_in_menu' => false,
'show_tagcloud' => false,
'show_admin_column' => true,
'query_var' => false,
'rewrite' => false,
'capabilities' => [
'manage_terms' => 'advanced_ads_edit_ads',
'edit_terms' => 'advanced_ads_edit_ads',
'delete_terms' => 'advanced_ads_edit_ads',
'assign_terms' => 'advanced_ads_edit_ads',
],
];
register_taxonomy(
Constants::TAXONOMY_GROUP,
Constants::POST_TYPE_AD,
apply_filters( 'advanced-ads-group-taxonomy-params', $args )
);
}
/**
* Placement description
*
* @return string
*/
public static function get_placement_description(): string {
return __( 'Placements are customizable ad spots on your site. Use them to see and change all the assigned ads and groups on this page. Furthermore, you can set up exclusive features like Cache Busting, Lazy Loading, AdBlocker fallbacks, or Parallax effects.', 'advanced-ads' );
}
/**
* Group description
*
* @return string
*/
public static function get_group_description(): string {
return __( 'Ad Groups are a flexible method to bundle ads. Use them to create ad rotations, run split tests, and organize your ads in the backend. An ad can belong to multiple ad groups.', 'advanced-ads' );
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* Modal.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds;
defined( 'ABSPATH' ) || exit;
/**
* Modal.
*/
class Modal {
/**
* Default values for the view file.
*
* @var array
*/
private $view_arguments = [
'modal_slug' => '',
'modal_content' => '',
'modal_title' => '',
'cancel_action' => true,
'close_action' => '',
'close_form' => '',
'close_validation' => '',
'template' => '',
'dismiss_button_styling' => '',
'container_styling' => '',
'background_styling' => '',
];
/**
* Create modal.
*
* @param array $arguments The passed view arguments, overwriting the default values.
* @param bool $render Whether to render the modal from the constructor. Defaults to true.
*
* @return Modal
*/
public static function create( array $arguments, $render = true ) {
return new Modal( $arguments, $render );
}
/**
* Create modal from file.
*
* @param array $arguments The passed view arguments, overwriting the default values.
* @param string $file File path to include content from.
* @param bool $render Whether to render the modal from the constructor. Defaults to true.
*
* @return Modal
*/
public static function create_from_file( array $arguments, $file, $render = true ) {
ob_start();
require $file;
$arguments['modal_content'] = ob_get_clean();
return new Modal( $arguments, $render );
}
/**
* Modal constructor.
*
* @param array $arguments The passed view arguments, overwriting the default values.
* @param bool $render Whether to render the modal from the constructor. Defaults to true.
*/
public function __construct( array $arguments, $render = true ) {
$this->view_arguments = array_intersect_key(
wp_parse_args(
array_map(
function ( $value ) {
return (string) $value;
},
$arguments
),
$this->view_arguments
),
$this->view_arguments
);
if ( $render ) {
$this->render();
}
}
/**
* Render the modal.
*
* @return void
*/
public function render() {
extract( $this->view_arguments, EXTR_OVERWRITE ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
$file = '' !== $this->view_arguments['template']
? $this->view_arguments['template']
: ADVADS_ABSPATH . 'admin/views/modal.php';
require $file;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Modules.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds;
use AdvancedAds\Interfaces\Module_Interface;
use AdvancedAds\Framework\Interfaces\Initializer_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Modules.
*/
class Modules implements Initializer_Interface {
/**
* Modules.
*
* @var array
*/
private $modules = [];
/**
* Running modules.
*
* @var array
*/
private $running = [];
/**
* Runs this initializer.
*
* @return void
*/
public function initialize(): void {
add_action( 'init', [ $this, 'load_modules' ], 0 );
}
/**
* Register a module.
*
* @param string $module Module class name.
*
* @return void
*/
public function register_module( string $module ): void {
$module = new $module();
$name = $module->get_name();
$this->modules[ $name ] = $module;
}
/**
* Load modules.
*
* @return void
*/
public function load_modules(): void {
foreach ( $this->modules as $module ) {
if ( $this->can_load( $module ) ) {
$module->load();
}
}
}
/**
* Check if a module can be loaded.
*
* @param Module_Interface $module Module object.
*
* @return bool
*/
private function can_load( Module_Interface $module ): bool {
$check = apply_filters( 'advanced-ads-can-load-module', true, $module );
if ( ! $check ) {
return false;
}
if ( in_array( $module->get_name(), $this->running, true ) ) {
return false;
}
$this->running[] = $module->get_name();
return true;
}
}

View File

@@ -0,0 +1,223 @@
<?php
/**
* Get and Set Options.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds;
use AdvancedAds\Framework\Utilities\Arr;
defined( 'ABSPATH' ) || exit;
/**
* This class is used to get and set plugin options.
*/
class Options {
/**
* Hold plugin options.
*
* @var array
*/
private $options = [];
/**
* Original DB option values.
*
* @var array
*/
private $raw_options = [];
/**
* Map of key to their respective option name.
*
* @var array
*/
private $options_map = [
'advanced-ads' => ADVADS_SLUG,
'adsense' => ADVADS_SLUG . '-adsense',
'adblocker' => ADVADS_SLUG . '-adblocker',
'ads-txt' => 'advanced_ads_ads_txt',
'notices' => ADVADS_SLUG . '-notices',
'privacy' => ADVADS_SLUG . '-privacy',
'pro' => ADVADS_SLUG . '-pro',
'tracking' => ADVADS_SLUG . '-tracking',
];
/**
* Retrieves the instance of the Options class.
*
* @return Options
*/
public static function instance() {
static $instance;
if ( null === $instance ) {
$instance = new Options();
}
return $instance;
}
/**
* Gets the value of a specific option.
*
* @param string $key Option suffix (advanced-ads-XXX).
* @param mixed $default_val Default value to return if the option is not set.
*
* @return mixed Array of option values, or the default value.
*/
public function get( $key, $default_val = false ) {
$keys = $this->normalize_key( $key );
$option_name = array_shift( $keys );
$values = $this->get_option( $option_name );
foreach ( $keys as $key ) {
$values = Arr::get( $values, $key, null );
}
return null === $values ? $default_val : $values;
}
/**
* Sets the value of a specific option.
*
* It checks if the method to set the option exists in the instance of the class and calls it if it does.
*
* @param string $key Option suffix (advanced-ads-XXX).
* @param array $value The value to set.
*
* @return array The new value of the option.
*/
public function set( $key, $value ): array {
$keys = $this->normalize_key( $key );
$option_name = array_shift( $keys );
$this->get_option( $option_name ); // Make sure the option is loaded.
$values = $this->raw_options[ $option_name ] ?? [];
$current = &$values;
foreach ( $keys as $key ) {
if ( ! Arr::has( $current, $key ) ) {
Arr::set( $current, $key, [] );
}
$current = &$current[ $key ];
}
$current = $value;
return $this->set_option( $option_name, $values );
}
/**
* Retrieves the value of an option identified by the given key.
*
* @param string $key Option name.
*
* @return array
*/
private function get_option( $key ): array {
// Early bail!!
if ( isset( $this->options[ $key ] ) ) {
return $this->options[ $key ];
}
// Check for getter method.
$method = 'get_' . $key;
if ( method_exists( $this, $method ) ) {
$this->options[ $key ] = $this->$method();
}
// Check in key map.
if ( isset( $this->options_map[ $key ] ) ) {
$this->options[ $key ] = get_option( $this->options_map[ $key ], [] );
}
$this->raw_options[ $key ] = $this->options[ $key ];
$this->options[ $key ] = $this->normalize_option( $this->options[ $key ] );
return $this->options[ $key ];
}
/**
* Sets the value of an option and updates the corresponding database entry.
*
* @param string $key Option name.
* @param mixed $value Option value.
*
* @return array The normalized option value.
*/
private function set_option( $key, $value ): array {
// Check for setter method.
$method = 'set_' . $key;
if ( method_exists( $this, $method ) ) {
$this->$method( $value );
}
// Check in key map.
if ( isset( $this->options_map[ $key ] ) ) {
update_option( $this->options_map[ $key ], $value );
}
$this->raw_options[ $key ] = $value;
$this->options[ $key ] = $this->normalize_option( $value );
return $this->options[ $key ];
}
/**
* Normalizes the given options array recursively.
*
* @param array $options Options array to be normalized.
*
* @return array The normalized options array.
*/
private function normalize_option( $options ): array {
if ( ! is_array( $options ) || empty( $options ) ) {
return [];
}
foreach ( $options as $key => $value ) {
$option[ $key ] = is_array( $value )
? $this->normalize_option( $value )
: $this->normalize_value( $value );
}
return $option;
}
/**
* Normalizes a given value.
*
* @param mixed $value Value to be normalized.
*
* @return mixed The normalized value.
*/
private function normalize_value( $value ) {
if ( 'true' === $value || 'on' === $value ) {
$value = true;
} elseif ( 'false' === $value || 'off' === $value ) {
$value = false;
} elseif ( '0' === $value || '1' === $value ) {
$value = intval( $value );
}
return $value;
}
/**
* Normalizes a given key by removing leading and trailing dots and splitting it into an array.
*
* @param string $key Key to be normalized.
*
* @return array The normalized key as an array.
*/
private function normalize_key( $key ) {
$key = trim( $key, '.' );
return explode( '.', $key );
}
}

View File

@@ -0,0 +1,340 @@
<?php
/**
* The plugin bootstrap.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds;
use AdvancedAds\Ads\Ads;
use AdvancedAds\Groups\Groups;
use Advanced_Ads_Admin_Licenses;
use AdvancedAds\Installation\Install;
use AdvancedAds\Placements\Placements;
defined( 'ABSPATH' ) || exit;
/**
* Plugin.
*
* Containers:
*
* @property Shortcodes $shortcodes Shortcodes handler.
* @property Assets_Registry $registry Assets registry.
* @property Framework\JSON $json JSON handler.
* @property Admin\Admin_Menu $screens Admin screens.
* @property Frontend\Ad_Renderer $renderer Ads renderer.
* @property Frontend\Manager $frontend Frontend manager.
* @property Importers\Manager $importers Importers manager.
*/
class Plugin extends Framework\Loader {
use Traits\Extras;
/**
* The ads container
*
* @var Ads
*/
public $ads = null;
/**
* The groups container
*
* @var Groups
*/
public $groups = null;
/**
* The placements container
*
* @var Placements
*/
public $placements = null;
/**
* Modules manager
*
* @var Modules
*/
public $modules = null;
/**
* Main instance
*
* Ensure only one instance is loaded or can be loaded.
*
* @return Plugin
*/
public static function get(): Plugin {
static $instance;
if ( null === $instance ) {
$instance = new Plugin();
$instance->setup();
}
return $instance;
}
/**
* Get plugin version
*
* @return string
*/
public function get_version(): string {
return ADVADS_VERSION;
}
/**
* Bootstrap plugin.
*
* @return void
*/
private function setup(): void {
$this->define_constants();
$this->includes_functions();
$this->includes();
$this->includes_rest();
$this->includes_admin();
$this->includes_frontend();
$this->includes_deprecated();
/**
* Old loading strategy
*
* TODO: need to remove it in future.
*/
// Public-Facing and Core Functionality.
\Advanced_Ads::get_instance();
\Advanced_Ads_ModuleLoader::loadModules( ADVADS_ABSPATH . 'modules/' ); // enable modules, requires base class.
if ( is_admin() ) {
Advanced_Ads_Admin_Licenses::get_instance();
}
add_action( 'plugins_loaded', [ $this, 'on_plugins_loaded' ], -1 );
add_action( 'widgets_init', [ $this, 'register_widgets' ] );
// Load it all.
$this->ads->initialize();
$this->groups->initialize();
$this->placements->initialize();
$this->modules->initialize();
$this->load();
}
/**
* Register the Advanced Ads classic Widget
*
* @return void
*/
public function register_widgets(): void {
register_widget( '\AdvancedAds\Widget' );
}
/**
* When WordPress has loaded all plugins, trigger the `advanced-ads-loaded` hook.
*
* @since 1.47.0
*
* @return void
*/
public function on_plugins_loaded(): void {
/**
* Action trigger after loading finished.
*
* @since 1.47.0
*/
do_action( 'advanced-ads-loaded' );
}
/**
* Define Advanced Ads constant
*
* @return void
*/
private function define_constants(): void {
$this->define( 'ADVADS_ABSPATH', dirname( ADVADS_FILE ) . '/' );
$this->define( 'ADVADS_PLUGIN_BASENAME', plugin_basename( ADVADS_FILE ) );
$this->define( 'ADVADS_BASE_URL', plugin_dir_url( ADVADS_FILE ) );
$this->define( 'ADVADS_SLUG', 'advanced-ads' );
// name for group & option in settings.
$this->define( 'ADVADS_SETTINGS_ADBLOCKER', 'advanced-ads-adblocker' );
// Deprecated Constants.
/**
* ADVADS_BASE
*
* @deprecated 1.47.0 use ADVADS_PLUGIN_BASENAME now.
*/
define( 'ADVADS_BASE', ADVADS_PLUGIN_BASENAME );
/**
* ADVADS_BASE_PATH
*
* @deprecated 1.47.0 use ADVADS_ABSPATH now.
*/
define( 'ADVADS_BASE_PATH', ADVADS_ABSPATH );
/**
* ADVADS_BASE_DIR
*
* @deprecated 1.47.0 Avoid global declaration of the constant used exclusively in `load_text_domain` function; use localized declaration instead.
*/
define( 'ADVADS_BASE_DIR', dirname( ADVADS_PLUGIN_BASENAME ) );
/**
* ADVADS_URL
*
* @deprecated 1.47.0 Deprecating the constant in favor of using the direct URL to circumvent costly `esc_url` function; please update code accordingly.
*/
define( 'ADVADS_URL', 'https://wpadvancedads.com/' );
}
/**
* Includes core files used in admin and on the frontend.
*
* @return void
*/
private function includes(): void {
$this->ads = new Ads();
$this->groups = new Groups();
$this->placements = new Placements();
$this->modules = new Modules();
// Common.
$this->register_initializer( Install::class );
$this->register_integration( Entities::class );
$this->register_integration( Assets_Registry::class, 'registry' );
$this->register_integration( Framework\JSON::class, 'json', [ 'advancedAds' ] );
$this->register_integration( Compatibility\Compatibility::class );
$this->register_integration( Compatibility\AAWP::class );
$this->register_integration( Compatibility\Peepso::class );
$this->register_integration( Post_Data::class );
$this->register_integration( Crons\Ads::class );
$this->register_integration( Shortcodes::class, 'shortcodes' );
$this->register_integration( Frontend\Debug_Ads::class );
}
/**
* Includes files used on the frontend.
*
* @return void
*/
private function includes_frontend(): void {
// Early bail!!
if ( is_admin() ) {
return;
}
$this->register_integration( Frontend\Ad_Renderer::class, 'renderer' );
$this->register_integration( Frontend\Manager::class, 'frontend' );
$this->register_integration( Frontend\Scripts::class );
}
/**
* Includes files used in admin.
*
* @return void
*/
private function includes_admin(): void {
// Early bail!!
if ( ! is_admin() ) {
return;
}
$this->register_integration( Compatibility\Capability_Manager::class );
$this->register_initializer( Upgrades::class );
$this->register_integration( Admin\Action_Links::class );
$this->register_integration( Admin\Admin_Menu::class, 'screens' );
$this->register_integration( Admin\Admin_Notices::class );
$this->register_integration( Admin\Assets::class );
$this->register_integration( Admin\Header::class );
$this->register_integration( Admin\Marketing::class );
$this->register_integration( Admin\Metabox_Ad::class );
$this->register_integration( Admin\Metabox_Ad_Settings::class );
$this->register_integration( Admin\Post_Types::class );
$this->register_integration( Admin\Screen_Options::class );
$this->register_integration( Admin\Shortcode_Creator::class );
$this->register_integration( Admin\TinyMCE::class );
$this->register_integration( Admin\WordPress_Dashboard::class );
$this->register_integration( Admin\Quick_Bulk_Edit::class );
$this->register_integration( Admin\Page_Quick_Edit::class );
$this->register_integration( Admin\Placement_Quick_Edit::class );
$this->register_integration( Importers\Manager::class, 'importers' );
$this->register_integration( Admin\AJAX::class );
$this->register_integration( Admin\Version_Control::class );
$this->register_integration( Admin\Upgrades::class );
$this->register_integration( Admin\Authors::class );
$this->register_integration( Admin\Settings::class );
$this->register_integration( Admin\Misc::class );
$this->register_integration( Admin\Post_List::class );
$this->register_integration( Admin\Placement\Bulk_Edit::class );
$this->register_integration( Admin\Addon_Updater::class );
if ( ! wp_doing_ajax() ) {
$this->register_integration( Admin\List_Filters::class, 'list_filters' );
}
}
/**
* Includes rest api files used in admin and on the frontend.
*
* @return void
*/
private function includes_rest(): void {
$this->register_route( Rest\Groups::class );
$this->register_route( Rest\Page_Quick_Edit::class );
$this->register_route( Rest\Placements::class );
$this->register_route( Rest\OnBoarding::class );
$this->register_route( Rest\Utilities::class );
}
/**
* Includes the necessary functions files.
*
* @return void
*/
private function includes_functions(): void {
require_once ADVADS_ABSPATH . 'includes/functions.php';
require_once ADVADS_ABSPATH . 'includes/functions-core.php';
require_once ADVADS_ABSPATH . 'includes/functions-conditional.php';
require_once ADVADS_ABSPATH . 'includes/functions-ad.php';
require_once ADVADS_ABSPATH . 'includes/functions-group.php';
require_once ADVADS_ABSPATH . 'includes/functions-placement.php';
require_once ADVADS_ABSPATH . 'includes/cap_map.php';
require_once ADVADS_ABSPATH . 'includes/default-hooks.php';
}
/**
* Includes deprecated files.
*
* @return void
*/
private function includes_deprecated(): void {
require_once ADVADS_ABSPATH . 'deprecated/ad_group.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_placements.php';
require_once ADVADS_ABSPATH . 'deprecated/Ad_Repository.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_type_abstract.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_type_content.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_type_dummy.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_type_group.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_type_image.php';
require_once ADVADS_ABSPATH . 'deprecated/ad_type_plain.php';
require_once ADVADS_ABSPATH . 'deprecated/ad-ajax.php';
require_once ADVADS_ABSPATH . 'deprecated/ad-debug.php';
require_once ADVADS_ABSPATH . 'deprecated/ad-expiration.php';
require_once ADVADS_ABSPATH . 'deprecated/ad-model.php';
require_once ADVADS_ABSPATH . 'deprecated/ad-select.php';
require_once ADVADS_ABSPATH . 'deprecated/ad.php';
require_once ADVADS_ABSPATH . 'deprecated/deprecated-functions.php';
require_once ADVADS_ABSPATH . 'deprecated/gadsense-dummy.php';
require_once ADVADS_ABSPATH . 'deprecated/Group_Repository.php';
require_once ADVADS_ABSPATH . 'deprecated/class-admin.php';
require_once ADVADS_ABSPATH . 'deprecated/class-advanced-ads-plugin.php';
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* Post Data.
*
* Standardize certain post data on save.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Post Data.
*/
class Post_Data implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'wp_untrash_post_status', [ $this, 'untrash_post_status' ], 10, 3 );
}
/**
* Ensure statuses are correctly reassigned when restoring ads that are previously expired.
*
* @param string $new_status The new status of the post being restored.
* @param int $post_id The ID of the post being restored.
* @param string $previous_status The status of the post at the point where it was trashed.
*
* @return null|string
*/
public function untrash_post_status( $new_status, $post_id, $previous_status ) {
$is_ours = in_array( get_post_type( $post_id ), [ Constants::POST_TYPE_AD, Constants::POST_TYPE_PLACEMENT ], true );
if ( ! $is_ours ) {
return $new_status;
}
if ( Constants::AD_STATUS_EXPIRED === $previous_status || 'draft' === $new_status ) {
return $previous_status;
}
return $new_status;
}
}

View File

@@ -0,0 +1,181 @@
<?php
/**
* Shortcodes.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Shortcodes.
*/
class Shortcodes implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_shortcode( 'the_ad', [ $this, 'render_ad' ] );
add_shortcode( 'the_ad_group', [ $this, 'render_group' ] );
add_shortcode( 'the_ad_placement', [ $this, 'render_placement' ] );
}
/**
* Renders an ad based on the provided attributes.
*
* @param array $atts The attributes for the ad.
*
* @return string The rendered ad.
*/
public function render_ad( $atts ): string {
$ad = wp_advads_get_ad( absint( $atts['id'] ?? 0 ) );
// Early bail!!
if ( ! $ad ) {
return '';
}
$atts = is_array( $atts ) ? $atts : [];
// Check if there is an inline attribute with or without value.
if ( isset( $atts['inline'] ) || in_array( 'inline', $atts, true ) ) {
$atts['inline_wrapper_element'] = true;
}
$atts = $this->prepare_shortcode_atts( $atts );
$this->set_shortcode_atts( $ad, $atts );
return get_the_ad( $ad, '', $atts );
}
/**
* Renders a group based on the provided attributes.
*
* @param array $atts The attributes for the group.
*
* @return string The rendered group.
*/
public function render_group( $atts ): string {
$group = wp_advads_get_group( absint( $atts['id'] ?? 0 ) );
// Early bail!!
if ( ! $group ) {
return '';
}
$atts = is_array( $atts ) ? $atts : [];
$atts = $this->prepare_shortcode_atts( $atts );
$this->set_shortcode_atts( $group, $atts );
return get_the_group( $group, '', $atts );
}
/**
* Renders a placement based on the provided attributes.
*
* @param array $atts The attributes for the placement.
*
* @return string The rendered placement.
*/
public function render_placement( $atts ): string {
$placement = wp_advads_get_placement( (string) ( $atts['id'] ?? '' ) );
// Early bail!!
if ( ! $placement ) {
return '';
}
$atts = is_array( $atts ) ? $atts : [];
$atts = $this->prepare_shortcode_atts( $atts );
$this->set_shortcode_atts( $placement, $atts );
return get_the_placement( $placement, '', $atts );
}
/**
* Prepare shortcode attributes.
*
* @param array $atts array with strings.
*
* @return array
*/
private function prepare_shortcode_atts( $atts ): array {
$result = [];
/**
* Prepare attributes by converting strings to multi-dimensional array
* Example: [ 'output__margin__top' => 1 ] => ['output']['margin']['top'] = 1
*/
if ( ! defined( 'ADVANCED_ADS_DISABLE_CHANGE' ) || ! ADVANCED_ADS_DISABLE_CHANGE ) {
foreach ( $atts as $attr => $data ) {
// Remove the prefix (change-ad__, change-group__, change-placement__).
$attr = preg_replace( '/^(change-ad|change-group|change-placement)__/', '', $attr );
$levels = explode( '__', $attr );
$last = array_pop( $levels );
$cur_lvl = &$result;
foreach ( $levels as $lvl ) {
if ( ! isset( $cur_lvl[ $lvl ] ) ) {
$cur_lvl[ $lvl ] = [];
}
$cur_lvl = &$cur_lvl[ $lvl ];
}
$cur_lvl[ $last ] = $data;
}
$result = array_diff_key(
$result,
[
'id' => false,
'blog_id' => false,
'ad_args' => false,
]
);
}
// Ad type: 'content' and a shortcode inside.
if ( isset( $atts['ad_args'] ) ) {
$result = array_merge( $result, json_decode( urldecode( $atts['ad_args'] ), true ) );
}
// Flat output array for Ad.
if ( isset( $result['output'] ) && is_array( $result['output'] ) ) {
$result = array_merge( $result, $result['output'] );
unset( $result['output'] );
}
// Special cases.
if ( isset( $result['tracking']['link'] ) ) {
$result['url'] = $result['tracking']['link'];
unset( $result['tracking']['link'] );
}
return $result;
}
/**
* Set shortcode attributes.
*
* @param object $entity The entity object.
* @param array $atts The attributes to set for the entity.
*
* @return void
*/
private function set_shortcode_atts( $entity, $atts ): void {
foreach ( $atts as $key => $value ) {
$entity->set_prop_temp( $key, $value );
}
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Upgrades.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds;
use AdvancedAds\Framework\Updates;
use AdvancedAds\Framework\Interfaces\Initializer_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Upgrades.
*/
class Upgrades extends Updates implements Initializer_Interface {
const DB_VERSION = '1.53.1';
/**
* Get updates that need to run.
*
* @since 1.0.0
*
* @return array
*/
public function get_updates(): array {
return [
'1.48.4' => 'upgrade-1.48.4.php',
'1.48.5' => 'upgrade-1.48.5.php',
'1.52.1' => 'upgrade-1.52.1.php',
'2.0.0' => 'upgrade-2.0.0.php',
'2.0.8' => 'upgrade-2.0.8.php',
];
}
/**
* Get folder path
*
* @since 1.0.0
*
* @return string
*/
public function get_folder(): string {
return ADVADS_ABSPATH . 'upgrades/';
}
/**
* Get plugin version number
*
* @since 1.0.0
*
* @return string
*/
public function get_version(): string {
return self::DB_VERSION;
}
/**
* Get plugin option name.
*
* @since 1.0.0
*
* @return string
*/
public function get_option_name(): string {
return 'advanced_ads_db_version';
}
/**
* Runs this initializer.
*
* @return void
*/
public function initialize(): void {
// Force run the upgrades.
$is_first_time = empty( $this->get_installed_version() );
$this->hooks();
if ( $is_first_time ) {
update_option( $this->get_option_name(), '1.0.0' );
add_action( 'admin_init', [ $this, 'perform_updates' ] );
}
}
}

View File

@@ -0,0 +1,303 @@
<?php
/**
* Advanced Ads Widget
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.47.0
*/
namespace AdvancedAds;
use AdvancedAds\Utilities\Data;
defined( 'ABSPATH' ) || exit;
/**
* Ad widget
*/
class Widget extends \WP_Widget {
/**
* The constructor.
*/
public function __construct() {
$prefix = wp_advads()->get_frontend_prefix();
$classname = $prefix . 'widget';
$widget_ops = [
'classname' => $classname,
'show_instance_in_rest' => true,
'description' => __( 'Display Ads and Ad Groups.', 'advanced-ads' ),
];
$control_ops = [];
$base_id = self::get_base_id();
parent::__construct( $base_id, 'Advanced Ads', $widget_ops, $control_ops );
add_filter( 'q2w3-fixed-widgets', [ $this, 'q2w3_replace_frontend_id' ] );
}
/**
* Echoes the widget content.
*
* @param array $args Display arguments including 'before_title', 'after_title', 'before_widget', and 'after_widget'.
* @param array $instance The settings for the particular instance of the widget.
*
* @return void
*/
public function widget( $args, $instance ): void {
$item_id = empty( $instance['item_id'] ) ? '' : $instance['item_id'];
$output = self::output( $item_id );
if ( ! $output ) {
return;
}
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
$before_widget = isset( $args['before_widget'] ) ? $args['before_widget'] : '';
$after_widget = isset( $args['after_widget'] ) ? $args['after_widget'] : '';
$before_widget = $this->maybe_replace_frontend_id( $before_widget, $instance );
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
echo $before_widget;
if ( ! empty( $title ) ) {
echo $args['before_title'] . $title . $args['after_title'];
}
echo $output;
echo $after_widget;
// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Updates a particular instance of a widget.
*
* This function should check that `$new_instance` is set correctly. The newly-calculated
* value of `$instance` should be returned. If false is returned, the instance won't be
* saved/updated.
*
* @param array $new_instance New settings for this instance as input by the user via WP_Widget::form().
* @param array $old_instance Old settings for this instance.
* @return array Settings to save or bool false to cancel saving.
*/
public function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = $new_instance['title'];
$instance['item_id'] = $new_instance['item_id'];
// Allow to remove/replace id for new widgets and if it was allowed earlier.
if ( [] === $old_instance || ! empty( $old_instance['remove-widget-id'] ) ) {
$instance['remove-widget-id'] = true;
}
return $instance;
}
/**
* Outputs the settings update form.
*
* @param array $instance Current settings.
*/
public function form( $instance ) {
$instance = wp_parse_args(
(array) $instance,
[
'title' => '',
'item_id' => '',
]
);
$elementid = $instance['item_id'];
$title = wp_strip_all_tags( $instance['title'] );
$items = array_merge( self::items_for_select(), self::widget_placements_for_select() );
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'advanced-ads' ); ?></label>
<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<select id="<?php echo esc_attr( $this->get_field_id( 'item_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'item_id' ) ); ?>">
<option value=""><?php esc_html_e( '--empty--', 'advanced-ads' ); ?></option>
<?php if ( isset( $items['placements'] ) ) : ?>
<optgroup label="<?php esc_html_e( 'Placements', 'advanced-ads' ); ?>">
<?php foreach ( $items['placements'] as $_item_id => $_item_title ) : ?>
<option value="<?php echo esc_attr( $_item_id ); ?>" <?php selected( $_item_id, $elementid ); ?>><?php echo esc_attr( $_item_title ); ?></option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
<?php if ( isset( $items['groups'] ) ) : ?>
<optgroup label="<?php esc_html_e( 'Ad Groups', 'advanced-ads' ); ?>">
<?php foreach ( $items['groups'] as $_item_id => $_item_title ) : ?>
<option value="<?php echo esc_attr( $_item_id ); ?>" <?php selected( $_item_id, $elementid ); ?>><?php echo esc_html( $_item_title ); ?></option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
<?php if ( isset( $items['ads'] ) ) : ?>
<optgroup label="<?php esc_html_e( 'Ads', 'advanced-ads' ); ?>">
<?php foreach ( $items['ads'] as $_item_id => $_item_title ) : ?>
<option value="<?php echo esc_attr( $_item_id ); ?>" <?php selected( $_item_id, $elementid ); ?>><?php echo esc_html( $_item_title ); ?></option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
</select>
<?php
$this->display_hints( $elementid );
}
/**
* Display hints related to the currently selected item in the dropdown.
*
* @param string $elementid Currently selected item.
*/
private function display_hints( $elementid ) {
$elementid_parts = explode( '_', $elementid );
if ( ! isset( $elementid_parts[1] ) || 'group' !== $elementid_parts[0] ) {
return;
}
$group = wp_advads_get_group( absint( $elementid_parts[1] ) );
if ( ! $group ) {
return;
}
echo $group->get_hints_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Get items for widget select field
*
* @return array $select items for select field.
*/
public static function items_for_select() {
return Data::items_for_select( [ 'placements' => false ] );
}
/**
* Get widget placements for select field
*
* @return array $items for select field.
*/
public static function widget_placements_for_select() {
$select = [];
$placements = wp_advads_get_placements();
foreach ( $placements as $key => $placement ) {
if ( $placement->is_type( [ 'sidebar_widget', 'default' ] ) ) {
$select['placements'][ 'placement_' . $key ] = $placement->get_title();
}
}
if ( ! empty( $select['placements'] ) ) {
asort( $select['placements'] );
}
return $select;
}
/**
* Return content of an in a widget
*
* @param string $id slug of the display.
* @return bool|string
*/
public static function output( $id = '' ) {
// Early bail!!
if ( empty( $id ) ) {
return;
}
$item = explode( '_', $id, 2 );
if ( isset( $item[1] ) ) {
$item_id = $item[1];
}
if ( empty( $item_id ) ) {
return;
}
$func = 'get_the_' . $item[0];
return $func( absint( $item_id ) );
}
/**
* Get the base id of the widget
*
* @return string
*/
public static function get_base_id() {
$options = \Advanced_Ads::get_instance()->options();
// deprecated to keep previously changed prefixed working.
$prefix2 = ( isset( $options['id-prefix'] ) && '' !== $options['id-prefix'] ) ? $options['id-prefix'] : 'advads_ad_';
return $prefix2 . 'widget';
}
/**
* Get frontend widget id.
*
* @param int $number Unique ID number of the current widget instance.
* @return string
*/
private function get_frontend_id( $number ) {
$prefix = wp_advads()->get_frontend_prefix();
return $prefix . 'widget-' . $number;
}
/**
* Make it harder for ad blockers to block the widget.
* removes the pre-defined widget ID (e.g., advads_ad_widget-20) and replaces it with one that uses the individual frontend prefix
*
* @param string $before_widget content before the widget.
* @param array $instance Settings for the current widget instance.
* @return string $before_widget
*/
private function maybe_replace_frontend_id( $before_widget, $instance ) {
if (
! empty( $instance['remove-widget-id'] )
|| defined( 'JNEWS_THEME_ID' ) // the JNews theme overrides the widget ID and resets it, so we target this specifically.
) {
$pattern = '#\sid=("|\')[^"\']+["\']#';
if (
( defined( 'ADVANCED_ADS_SHOW_WIDGET_ID' ) && ADVANCED_ADS_SHOW_WIDGET_ID )
|| ! empty( $instance['q2w3_fixed_widget'] )
) {
// Replace id.
$number = ! empty( $this->number ) ? $this->number : '';
$before_widget = preg_replace( $pattern, ' id=$01' . $this->get_frontend_id( $number ) . '$01', $before_widget );
} else {
// Remove id.
$before_widget = preg_replace( $pattern, '', $before_widget );
}
}
return $before_widget;
}
/**
* Provide the 'Q2W3 Fixed Widget' plugin with the new frontend widget id.
*
* @param array $sidebars_widgets existing sidebar widgets.
* @return array $sidebars_widgets
*/
public function q2w3_replace_frontend_id( $sidebars_widgets ) {
foreach ( $sidebars_widgets as $sidebar => $widgets ) {
foreach ( $widgets as $k => $widget ) {
// after Fixed Widget 5.3.0, the widget option includes '#'. It didnt before. We store the information since we need it later again.
$has_hash = strpos( $widget, '#' ) !== false;
$widget = str_replace( '#', '', $widget );
$pos = strrpos( $widget, '-' );
$option_name = substr( $widget, 0, $pos );
$number = substr( $widget, $pos + 1 );
if ( self::get_base_id() === $option_name ) {
$widget_options = get_option( 'widget_' . $option_name );
if ( ! empty( $widget_options[ $number ]['remove-widget-id'] ) ) {
// Add a hash if the widget had one before. See comment above.
$sidebars_widgets[ $sidebar ][ $k ] = $has_hash ? ( '#' . $this->get_frontend_id( $number ) ) : $this->get_frontend_id( $number );
}
}
}
}
return $sidebars_widgets;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* AAWP Compatibility.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Compatibility;
use AdvancedAds\Abstracts\Ad;
use AdvancedAds\Interfaces\Ad_Interface;
defined( 'ABSPATH' ) || exit;
/**
* AAWP Ad.
*/
class AAWP_Ad extends Ad implements Ad_Interface {
/**
* Prepare output for frontend.
*
* @return string
*/
public function prepare_frontend_output(): string {
$display_variant = $this->get_prop( 'display_variant' );
if ( empty( $display_variant ) ) {
return '';
}
switch ( $display_variant ) {
case 'box':
$next_input = $this->get_prop( 'asin' );
break;
case 'bestseller':
case 'new':
$next_input = $this->get_prop( 'keywords' );
break;
default:
$next_input = '';
}
$template = ! empty( $this->get_prop( 'template' ) ) ? $this->get_prop( 'template' ) : 'default';
$shortcode = '[' . aawp_get_shortcode() . ' ' . $display_variant . '="' . $next_input . '"';
if ( 'bestseller' === $display_variant || 'new' === $display_variant ) {
$shortcode = $shortcode . ' items="' . $this->get_prop( 'items' ) . '"';
}
$shortcode = $shortcode . ' template="' . $template . '"]';
return do_shortcode( $shortcode );
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* AAWP Compatibility.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Compatibility;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* AAWP.
*/
class AAWP implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_filter( 'advanced-ads-ad-types', [ $this, 'ad_type' ], 30 );
}
/**
* Add AAWP ad type to Advanced Ads.
*
* @param array $types ad types.
*
* @return array
*/
public function ad_type( $types ): array {
if ( isset( $types['aawp'] ) && 'Advanced_Ads_Ad_Type_Abstract' === get_parent_class( $types['aawp'] ) ) {
$advanced_ads_aawp = $types['aawp'];
unset( $types['aawp'] );
ob_start();
$advanced_ads_aawp->render_icon( null );
$icon = ob_get_clean();
$types['aawp'] = [
'id' => 'aawp',
'title' => $advanced_ads_aawp->title,
'description' => $advanced_ads_aawp->description,
'is_upgrade' => false,
'icon' => $icon,
'classname' => AAWP_Ad::class,
'render_parameters' => [ $advanced_ads_aawp, 'render_parameters' ],
];
}
return $types;
}
}

View File

@@ -0,0 +1,111 @@
<?php
/**
* This class is responsible for fixing compatibility issues in admin area.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.48.2
*/
namespace AdvancedAds\Compatibility;
use AdvancedAds\Constants;
use AdvancedAds\Utilities\Conditional;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Admin Compatibility.
*/
class Admin_Compatibility implements Integration_Interface {
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
add_action( 'admin_enqueue_scripts', [ $this, 'dequeue_jnews_style' ], 100 );
add_action( 'quads_meta_box_post_types', [ $this, 'fix_wpquadspro_issue' ], 11 );
add_filter( 'wpml_admin_language_switcher_active_languages', [ $this, 'wpml_language_switcher' ] );
// Hide from WPML translation settings.
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
add_filter( 'get_translatable_documents', [ $this, 'wpml_hide_from_translation' ], 10, 1 );
}
}
/**
* 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;
}
/**
* Dequeue J-NEWS styles to prevent layout issues.
*
* @return void
*/
public function dequeue_jnews_style(): void {
if ( ! Conditional::is_screen_advanced_ads() || ! defined( 'JNEWS_THEME_URL' ) ) {
return;
}
wp_dequeue_style( 'jnews-admin' );
}
/**
* Show only all languages in language switcher on Advanced Ads pages if ads and groups are translated
*
* @param array $active_languages languages that can be used in language switcher.
*
* @return array
*/
public function wpml_language_switcher( $active_languages ): array {
global $sitepress;
$screen = get_current_screen();
// Are we on group edit page and ad group translations are disabled.
if ( isset( $screen->id ) && 'advanced-ads_page_advanced-ads-groups' === $screen->id ) {
$translatable_taxonomies = $sitepress->get_translatable_taxonomies();
if ( ! is_array( $translatable_taxonomies ) || ! in_array( 'advanced_ads_groups', $translatable_taxonomies, true ) ) {
return [];
}
}
// If ad post type is translatable.
if ( isset( $screen->id ) && in_array( $screen->id, [ 'edit-advanced_ads', 'advanced_ads' ], true ) ) {
$translatable_documents = $sitepress->get_translatable_documents();
if ( empty( $translatable_documents['advanced_ads'] ) ) {
return [];
}
}
return $active_languages;
}
/**
* Hide post type from WPML translatable documents.
*
* @param array $documents Array of translatable documents.
*
* @return array Modified array.
*/
public function wpml_hide_from_translation( $documents ): array {
if ( isset( $documents[ Constants::POST_TYPE_PLACEMENT ] ) ) {
unset( $documents[ Constants::POST_TYPE_PLACEMENT ] );
}
return $documents;
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Integrates Advanced Ads capabilities with third party role manager plugins.
*
* @package AdvancedAds
* @author Advanced Ads <info@wpadvancedads.com>
* @since 1.50.0
*/
namespace AdvancedAds\Compatibility;
use AdvancedAds\Constants;
use AdvancedAds\Installation\Capabilities;
use AdvancedAds\Framework\Interfaces\Integration_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Capability Manager.
*
* Integrates with: Members
* Integrates with: User Role Editor
* Integrates with: PublishPress Capabilities
*/
class Capability_Manager implements Integration_Interface {
/**
* Group name.
*
* @var string
*/
const GROUP = 'advanced-ads';
/**
* Capability manager.
*
* @var Capabilities
*/
private $capability_manager = null;
/**
* Hook into WordPress.
*
* @return void
*/
public function hooks(): void {
$this->capability_manager = new Capabilities();
// Integrates with: Members.
add_action( 'members_register_caps', [ $this, 'register_members_caps' ] );
add_action( 'members_register_cap_groups', [ $this, 'register_members_groups' ] );
// Integrates with: User Role Editor.
add_filter( 'ure_capabilities_groups_tree', [ $this, 'register_ure_groups' ] );
add_filter( 'ure_custom_capability_groups', [ $this, 'register_ure_caps' ], 10, 2 );
// Integrates with: PublishPress Capabilities.
add_filter( 'cme_plugin_capabilities', [ $this, 'register_publishpress_caps' ] );
}
/**
* Get the name of the integration.
*
* @return string
*/
public function get_name(): string {
return __( 'Advanced Ads', 'advanced-ads' );
}
/**
* Register Members groups.
*
* @return void
*/
public function register_members_groups(): void {
members_register_cap_group(
self::GROUP,
[
'label' => $this->get_name(),
'priority' => 10,
'icon' => 'dashicons-editor-textcolor',
'caps' => array_keys( $this->capability_manager->get_capabilities() ),
]
);
// Remove post types groups.
members_unregister_cap_group( 'type-' . Constants::POST_TYPE_AD );
members_unregister_cap_group( 'type-' . Constants::POST_TYPE_PLACEMENT );
}
/**
* Register members capabilities.
*
* @return void
*/
public function register_members_caps(): void {
$capabilities = $this->capability_manager->get_capabilities();
foreach ( $capabilities as $cap => $label ) {
members_register_cap(
$cap,
[
'label' => $label,
'group' => self::GROUP,
]
);
}
}
/**
* Register URE groups.
*
* @param array $groups Groups.
*
* @return array
*/
public function register_ure_groups( array $groups ): array {
$groups = (array) $groups;
$groups[ self::GROUP ] = [
'caption' => $this->get_name(),
'parent' => 'custom',
'level' => 2,
];
return $groups;
}
/**
* Register URE capabilities.
*
* @param array $groups Current capability groups.
* @param string $cap_id Capability identifier.
*
* @return array
*/
public function register_ure_caps( $groups, $cap_id ): array {
if ( array_key_exists( $cap_id, $this->capability_manager->get_capabilities() ) ) {
$groups = (array) $groups;
$groups[] = self::GROUP;
}
return $groups;
}
/**
* Register PublishPress capabilities.
*
* @param array $caps Capabilities.
*
* @return array
*/
public function register_publishpress_caps( array $caps ): array {
$caps[ $this->get_name() ] = array_keys( $this->capability_manager->get_capabilities() );
return $caps;
}
}

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