base_fields = apply_filters( 'adpresso/placements/fields/base', [ [ 'id' => 'placement_type', 'type' => 'image_group', 'sanitize_type' => 'key', 'default' => 'content', 'label' => __( 'Select where you want to insert ads on your website.', 'adpresso' ), 'link' => 'https://wpadpresso.com/docs/placements/?utm_source=plugin-backend&utm_medium=manual-link&utm_campaign=placements&utm_content=settings-page', 'options_callback' => [ PlacementTypeRegistry::get(), 'get_all_types_for_select' ], 'box' => Placement_Type_Box::class, 'controls_dependencies' => true, ], [ 'id' => 'placement_item_id', 'type' => 'token_select', 'label' => __( 'Item (Ad/Group)', 'adpresso' ), 'description' => __( 'Choose an ad or group to show in this placement.', 'adpresso' ), 'isSingle' => true, 'options' => [ [ 'label' => __( 'Ads', 'adpresso' ), 'options' => adpresso_get_ad_repository()->get_ads_for_select(), 'class' => 'adpresso-color-ad' ], [ 'label' => __( 'Groups', 'adpresso' ), 'options' => adpresso_get_group_repository()->get_groups_for_select(), 'class' => 'adpresso-color-group' ], ], 'box' => Placement_General_Box::class, // Saved (and deleted when unassigned) by Placement_Type_Box. 'sanitize_type' => 'none', ], [ 'id' => 'placement_type_options_container', 'type' => 'div', 'html_id' => 'adpresso-placement-type-options-container', 'label' => __( 'Placement Type Options', 'adpresso' ), 'description' => __( 'Options for the selected placement type.', 'adpresso' ), ], [ 'id' => 'position', 'type' => 'positioning', 'label' => __( 'Defines how the ad interacts with surrounding content.', 'adpresso' ), 'box' => Layout_Box::class, 'meta_key' => Layout_Box::META_KEY_LAYOUT, 'sanitize_type' => 'key', // Meaningless for Popup - there's no surrounding content to float // against inside a fixed-position modal. 'depends_on' => [ [ 'field_id' => 'placement_type', 'operator' => 'is_not', 'value' => 'popup' ], ], 'positioning_options' => [ 'default' => [ 'title' => __( 'Theme Default', 'adpresso' ), 'description' => __( 'Inherits alignment from your active website theme.', 'adpresso' ), 'options' => [ 'none' => [ 'title' => __( 'None', 'adpresso' ) ], ], ], 'float' => [ 'title' => _x( 'Float', 'Layout options "Text Flow" heading', 'adpresso' ), 'description' => __( 'The ad is embedded into the text, allowing content to wrap around it.', 'adpresso' ), 'options' => [ 'left_float' => [ 'title' => __( 'Left', 'adpresso' ) ], 'right_float' => [ 'title' => __( 'Right', 'adpresso' ) ], ], ], 'block' => [ 'title' => _x( 'Block', 'Layout options "Text Flow" heading', 'adpresso' ), 'description' => __( 'The ad stands alone. The following content starts below.', 'adpresso' ), 'options' => [ 'left_nofloat' => [ 'title' => __( 'Left', 'adpresso' ) ], 'center_nofloat' => [ 'title' => __( 'Center', 'adpresso' ) ], 'right_nofloat' => [ 'title' => __( 'Right', 'adpresso' ) ], ], ], ], ], [ 'id' => 'margin', 'type' => 'spacing', 'label' => __( 'Margin', 'adpresso' ), 'description' => __( 'Define the margin around the ad in pixels.', 'adpresso' ), 'box' => Layout_Box::class, 'meta_key' => Layout_Box::META_KEY_LAYOUT, 'sanitize_type' => 'array:int', // Same reasoning as 'position' above - not applicable to Popup. 'depends_on' => [ [ 'field_id' => 'placement_type', 'operator' => 'is_not', 'value' => 'popup' ], ], 'options' => [ 'top' => [ 'label' => _x( 'Top', 'Ad positioning spacing label', 'adpresso' ) ], 'right' => [ 'label' => _x( 'Right', 'Ad positioning spacing label', 'adpresso' ) ], 'bottom' => [ 'label' => _x( 'Bottom', 'Ad positioning spacing label', 'adpresso' ) ], 'left' => [ 'label' => _x( 'Left', 'Ad positioning spacing label', 'adpresso' ) ], ], ], [ 'id' => 'label_override', 'type' => 'text', 'label' => __( 'Label override', 'adpresso' ), 'description' => __( 'Override the ad label with a custom one.', 'adpresso' ), 'box' => Layout_Box::class, 'sanitize_type' => 'text', 'placeholder' => 'e.g., "Sponsored"', ], [ 'id' => 'custom_classes', 'type' => 'text', 'sanitize_type' => 'html_classes', 'label' => __( 'Custom HTML Classes', 'adpresso' ), 'placeholder' => __( 'e.g., "placement-123"', 'adpresso' ), 'box' => Layout_Box::class, ], [ 'id' => 'inline_css', 'type' => 'text', 'sanitize_type' => 'safe_css', 'label' => __( 'Inline CSS', 'adpresso' ), 'placeholder' => __( 'e.g., "color:white;"', 'adpresso' ), 'box' => Layout_Box::class, ], [ 'id' => 'custom_placement_code', 'type' => 'textarea', 'sanitize_type' => 'custom_code', 'label' => __( 'Custom Code', 'adpresso' ), 'placeholder' => __( 'Optional field for custom scripts or code snippets to be injected immediately after the placement.', 'adpresso' ), 'box' => Layout_Box::class, 'premium' => true, 'scan_for_script_lifecycle' => true, ], [ 'id' => 'max_impressions_shortcut', 'type' => 'max_impressions', 'label' => __( 'Impression Limit', 'adpresso' ), 'description' => __( 'Limit how many times a visitor sees this placement within a time period.', 'adpresso' ), 'default' => [ 'enabled' => false, 'count' => 3, 'period_amount' => 1, 'period_unit' => 'day', 'cooldown_minutes' => 0 ], 'show_enabled_toggle' => true, // Scheduling_Frequency_Box::save() already writes this meta key itself before // delegating to the generic per-field loop; without a real sanitize_type the // loop's fallback (getString()) sees a non-scalar value and stomps it back to // an empty string. 'array:key' keeps the generic pass a harmless no-op re-write. 'sanitize_type' => 'array:key', 'box' => Scheduling_Frequency_Box::class, 'premium' => true, // Only meaningful for placement types with their own "appearance event" // (Popup, Ad Bar/Sticky) - a "normal" placement (Header, Footer, Content, ...) // renders passively whatever it's assigned, so a frequency limit belongs on // the assigned ad/group, not the slot itself (see design doc ยง08). A regular // placement that already had this configured before the restriction existed // keeps working exactly as before - depends_on only hides the field, it never // stops the underlying condition from being saved/enforced. 'depends_on' => [ [ 'field_id' => 'placement_type', 'operator' => 'is_one_of', 'value' => [ 'popup', 'ad_bar' ] ], ], ], ] ); $this->base_fields = array_merge( $this->base_fields, apply_filters( 'adpresso_placement_fallback_fields', [ [ 'id' => 'general_fallback', 'type' => 'token_select', 'label' => __( 'General fallback item', 'adpresso' ), 'description' => __( 'Choose a fallback for slots where no ad is available for other reasons, or when the consent fallback cannot be displayed. The selected ad is displayed regardless of privacy settings, so choose an ad that does not require consent, such as a static banner linking to your own content.', 'adpresso' ), 'isSingle' => true, 'options' => [ [ 'label' => __( 'Ads', 'adpresso' ), 'options' => adpresso_get_ad_repository()->get_ads_for_select(), 'class' => 'adpresso-color-ad' ], [ 'label' => __( 'Groups', 'adpresso' ), 'options' => adpresso_get_group_repository()->get_groups_for_select(), 'class' => 'adpresso-color-group' ], ], 'box' => Fallback_Box::class, ], ] ) ); } }