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.
*/