$placement->get_public_id(), "data-{$prefix}-instance" => uniqid( '', false ), ]; return self::render_attributes( $attrs ); } /** * Level 2: Generates the attributes for a group (collection). * * @param Entity $group The group object. * * @return string HTML-Attribute string */ public static function for_collection( Entity $group ): string { $prefix = self::get_prefix(); $attrs = [ "data-{$prefix}-collection" => $group->get_id(), ]; return self::render_attributes( $attrs ); } /** * Level 3: Generates the attributes for an ad. * This is the anchor for the JS tracking. * * @param Entity $ad The ad object. * @param array $context The Render-context (e.g. placement_id, group_id). * * @return string HTML-Attribute string */ public static function for_item( Entity $ad, array $context = [] ): string { $prefix = self::get_prefix(); // Item-ID for JS and Debugger $attrs = [ "data-{$prefix}-item" => $ad->get_id(), ]; $tracking_globally = (bool) Settings::get_setting( 'enable_module_tracking', false ); // TODO: get sense-full value $tracking_ad = $ad->get_meta( 'tracking_enabled', true ); if ( $tracking_globally && $tracking_ad ) { // 't' signalizes that the ad should be tracked. $attrs["data-{$prefix}-t"] = '1'; if ( ! empty( $context ) ) { foreach ( $context as $key => $value ) { if ( ! is_string( $value ) ) { continue; } // Translate keys in neutral ones. $safe_key = match ( $key ) { 'placement_id' => 'w-ctx', // Wrapper Context 'group_id' => 'c-ctx', // Collection Context default => sanitize_key( $key ), }; $attrs["data-{$prefix}-{$safe_key}"] = $value ?? 0; } } } return self::render_attributes( $attrs ); } /** * Level 4 (JS): Generates the attributes for the invisible Placeholder script (Client-Side Rendering). * * @param Placement $placement The placement object. * * @return string HTML-Attribute string */ public static function for_placeholder( Placement $placement ): string { $prefix = self::get_prefix(); $item = $placement->get_item(); $item_id = $item ? $item->get_id() : 0; $item_type = $item ? ( ( $item instanceof Group ) ? '2' : '1' ) : '0'; // 1 = Ad, 2 = Group, 0 = None $attrs = [ 'id' => sprintf( '%s-%s', $prefix, $placement->get_public_id() ), // Script will be removed from the DOM anyway. "data-{$prefix}-wrap" => $placement->get_public_id(), "data-{$prefix}-item" => $item_id, "data-{$prefix}-type" => $item_type, ]; // Add In-Content injection rules if ( $placement->get_type() === 'content' ) { $attrs["data-{$prefix}-tgt"] = $placement->get_meta( 'position_element', 'p' ); $attrs["data-{$prefix}-nth"] = max( 1, absint( $placement->get_meta( 'position_after', 1 ) ) ); $attrs["data-{$prefix}-mth"] = $placement->get_meta( 'position_method', 'after' ); } return self::render_attributes( $attrs ); } /** * Get the frontend prefix from the settings. */ private static function get_prefix(): string { return sanitize_html_class( Settings::get_setting( 'frontend_prefix', Constants::DEFAULT_FRONTEND_PREFIX ) ); } /** * Transform the array to a final HTML string. */ private static function render_attributes( array $attributes ): string { $html = []; foreach ( $attributes as $key => $value ) { // Key is always secure and doesn't need escaping. $html[] = sprintf( '%s="%s"', $key, esc_attr( (string) $value ) ); } return implode( ' ', $html ); } /** * Generates all the standard attributes (classes, ID, styles, data) for an ad item. * * @param Entity $ad The ad. * @param string $type_id The id of the ad type (e.g. 'html', 'html5'). * @param array $context The render context. * * @return string Final Attribute string (e.g. 'class="..." style="..." data-...') */ public static function get_standard_item_attributes( Entity $ad, string $type_id, array $context = [] ): string { $prefix = self::get_prefix(); $ad_id = $ad->get_id(); // CSS Classes $type_class = sprintf( '%s-type-%s', $prefix, sanitize_html_class( $type_id ) ); $custom_classes = sanitize_html_class( $ad->get_meta( 'custom_classes', '' ) ); $css_classes = sprintf( '%1$s-item %2$s %1$s-i-%3$d %4$s', $prefix, $type_class, $ad_id, $custom_classes ); // Custom ID $custom_html_id = $ad->get_meta( 'custom_id', '' ); // Styles, layout and alignment $inline_css = safecss_filter_attr( $ad->get_meta( 'inline_css', '' ) ); $alignment = safecss_filter_attr( $ad->get_layout_styles_string() ); $style = trim( $inline_css . $alignment ); $data_attributes = self::for_item( $ad, $context ); $attributes = []; if ( ! empty( $custom_html_id ) ) { $attributes[] = sprintf( 'id="%s"', esc_attr( $custom_html_id ) ); } $attributes[] = sprintf( 'class="%s"', esc_attr( trim( $css_classes ) ) ); if ( ! empty( $style ) ) { $attributes[] = sprintf( 'style="%s"', esc_attr( $style ) ); } return implode( ' ', $attributes ) . ' ' . $data_attributes; } }