Lecteur Markdown
BeamReactor_Widgets_Guide
BeamReactor Widgets - Quick Reference #
Definition #
Widget = raw HTML/CSS/JS output, NO BeamReactor UI wrapping (no headers/footers/menus)
Widgets are Plugin Sub-Elements #
A widget is not a standalone entity. It is a sub-element of its parent plugin,
and lives inside the plugin's `widget/` subdirectory:
plugins/pluginname/
pluginname.php # main plugin file
conf/pluginname.conf.inc.php
lib/pluginname.lib.inc.php
locale/pluginname.fr.inc.php
widget/
pluginname.widget.php # widget entry point
The widget entry point delegates to the plugin's main file using a sentinel value:
<?php
$_saved_obj = $obj;
$obj = 'widget';
include_once('plugins/pluginname/pluginname.php');
$obj = $_saved_obj;
The plugin's main file adapts its output based on `$obj`:
if($obj == 'pluginname.php') frameheader($dialplugindisplay);
// when $obj == 'widget': render bare output, no shell
Existing Widgets Without a Parent Plugin #
Some widgets in the codebase (`automated_bans`, `general_latency`, `general_performance`, etc.)
currently live in the old top-level `widgets/` directory and have no parent plugin.
Two valid approaches for these:
Option A — Absorb into a related plugin
If the widget is a display face for data owned by an existing plugin (e.g. `blogs_tagcloud`
belongs conceptually to `blogs`), move it into that plugin's `widget/` subdirectory.
Option B — Create a widget-only plugin
If the widget has no natural parent, create a minimal plugin that acts as its host:
plugins/general_latency/
conf/general_latency.conf.inc.php
widget/
general_latency.widget.php # self-contained, no main plugin file needed
The plugin exists solely to own the widget's conf and provide a clean namespace.
No `pluginname.php` is required if there is no full-UI mode.
The top-level `widgets/` directory has no place in the new architecture.
Use Cases #
- Embed in external sites (iframe/fetch)
- Data syndication ("share our content")
- Internal components (xdpplugin:// links in DTA docs)
- Embedding a plugin's functionality inside a page without the BeamReactor shell
Rules #
FORBIDDEN:
- `<html>`, `<head>`, `<body>` tags
- Headers, footers, navigation
- Hardcoded colors (use CSS vars only: `var(--primary, #fallback)`)
- Global JS variables
- Parent DOM manipulation
REQUIRED:
- Scope CSS to unique class (`.widget-name`)
- IIFE wrapper for JS
- All calls via XDP (never direct include)
- Sanitize all params
Why XDP? #
Security, stats, cache, audit, revocation = data sovereignty
Example Widget Code #
<?php
$slug = $_GET['slug'] ?? 'default';
$items = SQL::query("SELECT * FROM faq WHERE slug=?", [$slug], ['string']);
?>
<div class="faq-widget">
<?php foreach($items as $item): ?>
<div class="faq-item"><?= htmlspecialchars($item['text']) ?></div>
<?php endforeach; ?>
</div>
<style>
.faq-widget { color: var(--text, #333); }
</style>
<script>(function(){ /* scoped code */ })();</script>
Usage #
Embedded in a PHP page/doc:
<?php include(ROOT_DIR.'/plugins/pluginname/pluginname.php'); ?>
Via HTTP (no BeamReactor shell):
index.php?obj=pluginname.widget
External (third-party sites):
<iframe src="https://site.com/index.php?obj=widgetname.widget¶m=val"></iframe>
<!-- OR via fetch -->
<div id="target"></div>
<script>
fetch('https://site.com/index.php?obj=widgetname.widget¶m=val')
.then(r => r.text())
.then(html => document.getElementById('target').innerHTML = html);
</script>
Best Practices #
✅ Semantic HTML, scoped CSS, validated params, fallback content
❌ Global vars, hardcoded styles, direct file access, parent manipulation