- Move all plugin files into breznflow/ subdirectory (matches BreznGEO structure) - Add README.md (English) and README.de.md (German) with full documentation - Add GPL-2.0 LICENSE file - Rewrite readme.txt: expanded description, FAQs, external services, changelog Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
185 lines
7.1 KiB
PHP
185 lines
7.1 KiB
PHP
<?php
|
|
/**
|
|
* Handles standalone embed page rendering for workflows.
|
|
*
|
|
* @package BreznFlow
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
namespace BreznFlow;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Serves a standalone HTML page for embedding a workflow via iframe
|
|
* when the breznflow_embed query parameter is present.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class EmbedHandler {
|
|
/**
|
|
* Registers the template_redirect hook for embed handling.
|
|
*
|
|
* @since 1.2.0
|
|
* @return void
|
|
*/
|
|
public function register(): void {
|
|
add_action( 'template_redirect', array( $this, 'handle_embed' ) );
|
|
}
|
|
|
|
/**
|
|
* Processes the embed request and outputs a standalone HTML page.
|
|
*
|
|
* @since 1.2.0
|
|
* @return void
|
|
*/
|
|
public function handle_embed(): void {
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public read-only embed endpoint, no state change; only serves published data.
|
|
if ( ! isset( $_GET['breznflow_embed'] ) ) {
|
|
return;
|
|
}
|
|
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public read-only embed endpoint.
|
|
$post_id = (int) $_GET['breznflow_embed'];
|
|
if ( $post_id <= 0 ) {
|
|
status_header( 400 );
|
|
exit;
|
|
}
|
|
|
|
$settings = array_merge( Admin\SettingsPage::get_defaults(), get_option( 'breznflow_settings', array() ) );
|
|
|
|
if ( empty( $settings['allow_embed'] ) ) {
|
|
status_header( 403 );
|
|
exit;
|
|
}
|
|
|
|
$post = get_post( $post_id );
|
|
if ( ! $post || 'breznflow_workflow' !== $post->post_type || 'publish' !== $post->post_status ) {
|
|
status_header( 404 );
|
|
exit;
|
|
}
|
|
|
|
$show_embed = (bool) get_post_meta( $post_id, '_breznflow_show_embed', true );
|
|
if ( ! $show_embed ) {
|
|
status_header( 403 );
|
|
exit;
|
|
}
|
|
|
|
$raw_json = get_post_meta( $post_id, '_breznflow_raw_json', true );
|
|
if ( ! $raw_json ) {
|
|
status_header( 404 );
|
|
exit;
|
|
}
|
|
|
|
$workflow = json_decode( $raw_json, true );
|
|
if ( ! is_array( $workflow ) ) {
|
|
status_header( 500 );
|
|
exit;
|
|
}
|
|
|
|
$allowed_themes = \BreznFlow\Features\ThemeRegistry::get_theme_ids();
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
|
$url_theme = isset( $_GET['theme'] ) ? sanitize_text_field( wp_unslash( $_GET['theme'] ) ) : '';
|
|
$theme = in_array( $url_theme, $allowed_themes, true ) ? $url_theme : ( $settings['default_theme'] ?? 'dark' );
|
|
$theme = in_array( $theme, $allowed_themes, true ) ? $theme : 'dark';
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public read-only embed page, no state change.
|
|
$show_minimap_embed = isset( $_GET['minimap'] ) ? ( '0' !== sanitize_text_field( wp_unslash( $_GET['minimap'] ) ) ) : true;
|
|
|
|
$body_bgs = array(
|
|
'dark' => '#1a1a2e',
|
|
'light' => '#eef2f7',
|
|
'minimal' => '#fafafa',
|
|
'tech' => '#0d1117',
|
|
'brezn' => '#001f4d',
|
|
);
|
|
$body_bg = $body_bgs[ $theme ] ?? '#1a1a2e';
|
|
|
|
// Set headers.
|
|
header( 'Content-Type: text/html; charset=utf-8' );
|
|
header( 'X-Robots-Tag: noindex, nofollow' );
|
|
header( 'X-Content-Type-Options: nosniff' );
|
|
header_remove( 'X-Frame-Options' );
|
|
|
|
$article_url = esc_url( get_permalink( $post_id ) );
|
|
$anchor_id = 'breznflow-' . $post_id;
|
|
$blog_name = esc_html( get_bloginfo( 'name' ) );
|
|
$blog_url = esc_url( home_url( '/' ) );
|
|
$title = esc_html( $post->post_title );
|
|
$css_url = esc_url( BREZNFLOW_URL . 'assets/renderer.css' ) . '?v=' . BREZNFLOW_VERSION;
|
|
$js_url = esc_url( BREZNFLOW_URL . 'assets/renderer.js' ) . '?v=' . BREZNFLOW_VERSION;
|
|
|
|
$inline_data = array(
|
|
array(
|
|
'id' => $post_id,
|
|
'workflow' => $workflow,
|
|
'mode' => 'visual',
|
|
'zoom' => 100,
|
|
'autofit_threshold' => (int) ( $settings['autofit_threshold'] ?? 30 ),
|
|
'show_title' => false,
|
|
'show_infobox' => false,
|
|
'show_download' => false,
|
|
'show_minimap' => $show_minimap_embed,
|
|
'show_share' => false,
|
|
'show_embed' => false,
|
|
'show_get_json' => false,
|
|
'max_code_lines' => (int) ( $settings['max_code_lines'] ?? 50 ),
|
|
'download_label' => '',
|
|
'download_url' => '',
|
|
'theme' => $theme,
|
|
),
|
|
);
|
|
|
|
$icons_json = wp_json_encode( Features\NodeTypeRegistry::get_registry() );
|
|
$data_json = wp_json_encode( $inline_data );
|
|
$i18n_json = wp_json_encode( Shortcode::get_js_i18n() );
|
|
|
|
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- intentional standalone HTML output; all dynamic values escaped above
|
|
?><!DOCTYPE html>
|
|
<html lang="<?php echo esc_attr( get_bloginfo( 'language' ) ); ?>" data-theme="<?php echo esc_attr( $theme ); ?>">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="robots" content="noindex, nofollow">
|
|
<title><?php echo $title; ?></title>
|
|
<link rel="stylesheet" href="<?php echo $css_url; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- standalone embed page, no wp_head(). ?>">
|
|
<?php foreach ( \BreznFlow\Features\ThemeRegistry::BUILTIN as $bf_embed_id => $bf_embed_name ) : ?>
|
|
<link rel="stylesheet" href="<?php echo esc_url( \BreznFlow\Features\ThemeRegistry::get_builtin_url( $bf_embed_id ) ) . '?v=' . BREZNFLOW_VERSION; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- standalone embed page, no wp_head(). ?>">
|
|
<?php endforeach; ?>
|
|
<?php $embed_custom_css = \BreznFlow\Features\ThemeRegistry::get_custom_theme_css(); if ( $embed_custom_css ) : ?>
|
|
<style><?php echo wp_strip_all_tags( $embed_custom_css ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS from validated color tokens, stripped of HTML tags. ?></style>
|
|
<?php endif; ?>
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; }
|
|
html, body { margin: 0; padding: 0; height: 100%; background: <?php echo esc_attr( $body_bg ); ?>; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
|
|
body { display: flex; flex-direction: column; }
|
|
#breznflow-embed-viewer { flex: 1; min-height: 0; }
|
|
#breznflow-embed-viewer .breznflow-embed { height: 100%; border-radius: 0; border: none; }
|
|
#breznflow-embed-footer { padding: 6px 12px; background: #111; border-top: 1px solid #333; font-size: 11px; color: #888; display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
|
|
#breznflow-embed-footer a { color: #aaa; text-decoration: none; }
|
|
#breznflow-embed-footer a:hover { color: #e0e0e0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="breznflow-embed-viewer">
|
|
<div id="breznflow-wrap-<?php echo (int) $post_id; ?>" class="breznflow-embed" data-id="<?php echo (int) $post_id; ?>"></div>
|
|
</div>
|
|
<footer id="breznflow-embed-footer">
|
|
<a href="<?php echo $article_url; ?>#<?php echo esc_attr( $anchor_id ); ?>"><?php echo $title; ?></a>
|
|
<span>•</span>
|
|
<span><?php esc_html_e( 'Source:', 'breznflow' ); ?> <a href="<?php echo $blog_url; ?>"><?php echo $blog_name; ?></a></span>
|
|
</footer>
|
|
<script>
|
|
var breznflowData = <?php echo $data_json; ?>;
|
|
var breznflowIcons = <?php echo $icons_json; ?>;
|
|
var breznflowI18n = <?php echo $i18n_json; ?>;
|
|
</script>
|
|
<script src="<?php echo $js_url; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- standalone embed page, no wp_head(). ?>"></script>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
// phpcs:enable
|
|
exit;
|
|
}
|
|
}
|