get_item(); // Prepare debugging. $snapshot = RuntimeDebugger::build_snapshot( $placement, $item ); $snapshot['execution']['trigger'] = $trigger; $snapshot['execution']['hook_fired'] = true; if ( $trigger === 'shortcode' && $placement->get_type() !== 'manual' ) { // Wir erlauben das Rendern von nicht-manuellen Placements via Shortcode (z.B. für A2 Takeover), // warnen aber im Debugger, da dies nicht der Standardweg ist. $snapshot['execution']['warning'] = __( 'Rendering a non-manual placement via shortcode', 'adpresso' ); } elseif ( $trigger !== 'shortcode' && $placement->get_type() !== 'manual' ) { $snapshot['execution']['status'] = 'error'; $snapshot['execution']['skip_reason'] = __( 'Wrong usage of manual placement', 'adpresso' ); RuntimeDebugger::add_log_entry( $placement_id, $snapshot ); RuntimeDebugger::mark_output_triggered( $placement_id ); NotificationManager::get()->register( 'adpresso-manual-placement', __( 'Wrong usage of manual placement', 'adpresso' ), [ 'location' => 'admin_bar', 'context' => 'adpresso-notifications', 'type' => 'info', ] ); return ''; } // RENDERING LOGIK if ( ! $placement_data ) { // Fallback-Loading for physical manual placements that haven't been processed yet. $item = $placement->get_item(); $requires_frontend_logic = apply_filters( 'adpresso_requires_frontend_logic', $placement->requires_frontend_logic || ( $item && $item->requires_frontend_logic ), $placement, $item ); if ( $requires_frontend_logic ) { $output = $placement->get_placeholder_html(); $snapshot['execution']['output_mode'] = 'js'; // We need to make sure, that virtual config is available here. $uid = $placement->get_uid(); $config = $placement->frontend_config_data; $config['uid'] = $uid; $config['item'] = $item ? $item->frontend_config_data : null; FrontendHooks::$virtual_js_configs[ $uid ] = $config; } else { $renderer = PlacementRenderer::get_instance(); $output = $renderer->render( $placement, '' ); $snapshot['execution']['output_mode'] = 'fallback'; } } else { $snapshot['execution']['output_mode'] = $placement_data['output_mode'] ?? 'unknown'; if ( ! empty( $placement_data['skip_reason'] ) ) { $snapshot['execution']['skip_reason'] = $placement_data['skip_reason']; if ( $placement_data['output_mode'] === 'skipped' ) { $snapshot['execution']['status'] = 'skipped'; } } $output = match ( $placement_data['output_mode'] ) { 'ssr' => $placement->render_with_wrapper( $placement_data['content'] ), 'js' => $placement->get_placeholder_html(), default => '', }; } RuntimeDebugger::add_log_entry( $placement_id, $snapshot ); RuntimeDebugger::mark_output_triggered( $placement_id ); return $output; } /** * Renders an ad or group with a virtual placement. * * @param Entity $entity The Ad- or Group-Object. * @param string $trigger The origin of the call (e.g. 'shortcode', 'manual_block'). * * @return string The rendered HTML. */ public static function render_virtual_entity( Entity $entity, string $trigger, array $shortcode_atts = [] ): string { if ( ! ( $entity instanceof Ad || $entity instanceof Group ) ) { return ''; } $prefix = $entity instanceof Ad ? 'ad' : 'group'; $placement = PlacementFactory::make_virtual( $prefix, $entity->get_id() ); self::apply_shortcode_overrides( $placement, $shortcode_atts ); $placement->processBackendData(); $uid = $placement->get_uid(); $placement->frontend_config_data['uid'] = $uid; $context = [ 'placement_id' => $placement->get_id(), 'placement' => $placement, 'is_raw_output' => ! $placement->should_wrap(), ]; if ( ! $entity->can_be_displayed() ) { $entity->is_backend_valid = false; $entity->skip_reason = 'Display once limit reached (Virtual Placement)'; } $output = ''; if ( $entity->is_backend_valid ) { $requires_frontend_logic = apply_filters( 'adpresso_requires_frontend_logic', $entity->requires_frontend_logic, $placement, $entity ); if ( $requires_frontend_logic ) { $output = $placement->get_placeholder_html(); // We need to make sure that virtual config is available here. FrontendHooks::$virtual_js_configs[ $uid ] = $placement->frontend_config_data; $entity->mark_as_rendered(); } else { $renderer = PlacementRenderer::get_instance(); try { $inner_content = $entity->get_type_object()->output( $entity, $context ); $output = $renderer->render( $placement, $inner_content ); if ( ! empty( $inner_content ) ) { $entity->mark_as_rendered(); } } catch ( \Exception $e ) { $output = ''; $entity->skip_reason = 'Rendering exception: ' . $e->getMessage(); } } } $snapshot = RuntimeDebugger::build_snapshot( $placement, $entity ); $snapshot['execution']['trigger'] = $trigger . '_virtual'; $snapshot['execution']['output_mode'] = ( $requires_frontend_logic ?? $entity->requires_frontend_logic ) ? 'js' : 'ssr'; $snapshot['execution']['hook_fired'] = true; RuntimeDebugger::add_log_entry( $uid, $snapshot ); RuntimeDebugger::mark_output_triggered( $uid ); return $output; } /** * Sends the gathered virtual configs to the global JS object. * * @return void */ public function localize_virtual_configs(): void { if ( empty( self::$virtual_js_configs ) ) { return; } ?> classes_override = sanitize_text_field( $atts['class'] ); } $css = []; if ( ! empty( $atts['margin'] ) ) { $css[] = 'margin: ' . esc_attr( $atts['margin'] ) . ' !important;'; } if ( ! empty( $atts['align'] ) ) { $align = strtolower( sanitize_text_field( $atts['align'] ) ); if ( $align === 'center' ) { $css[] = 'margin-left: auto !important; margin-right: auto !important; display: block !important;'; $placement->classes_override .= ' adpresso-align-center'; } elseif ( $align === 'left' ) { $css[] = 'float: left !important;'; $placement->classes_override .= ' adpresso-align-left'; } elseif ( $align === 'right' ) { $css[] = 'float: right !important;'; $placement->classes_override .= ' adpresso-align-right'; } } if ( ! empty( $css ) ) { $placement->css_override = implode( ' ', $css ); } if ( isset( $atts['inline'] ) || in_array( 'inline', $atts, true ) ) { $placement->is_inline_override = true; } } }