Markdown Reader
Beamreactor Profile System
BeamReactor – Profile System & Profile Extensions #
Version: 1.0
Date: 2026.03.24
Audience: Plugin developers for the BeamReactor CMS
Scope: User profiles, ProfileManager class, profile extension mechanism
Version: 1.0
Date: 2026.03.24
Audience: Plugin developers for the BeamReactor CMS
Scope: User profiles, ProfileManager class, profile extension mechanism
The profile system handles user profile display and editing through three core files:
| File | Role |
|---|---|
members/view_profile.php | Public profile display |
members/edit_profile.php | Profile editing (logged-in user) |
lib/profilemanager/ProfileManager.class.php | Profile data operations & extension discovery |
Profiles are stored in the main users table ($cfg['dbtable']).
Namespace: Beamreactor\Members\ProfileManager
Auto-loaded via CoreAutoloader.
use Beamreactor\Members\ProfileManager;
$profileManager = new ProfileManager($cfg);
| Method | Returns | Description |
|---|---|---|
getProfileBySession(array $session) | array|false | Fetch profile matching session data |
getProfileByUsername(string $username) | array|false | Fetch profile by username |
getProfileById(int $userid) | array|false | Fetch profile by user ID |
updateEmail(int $userid, string $email) | bool | Update email address |
updateInfo(int $userid, string $info) | bool | Update bio/info text |
updateSkin(int $userid, string $skin) | bool | Change skin preference |
updateLanguage(int $userid, string $language) | bool | Change language preference |
updateNewsletter(int $userid, int $enabled) | bool | Toggle newsletter subscription |
updatePrivacy(int $userid, int $privacy) | bool | Set privacy level (0-4) |
updateAvatar(string $username, array $file, array $cfg) | array | Upload and process avatar |
validateProfileData(array $data) | array | Validate profile fields |
getAvailableSkins() | array | List installed skins |
getAvailableLanguages() | array | List available languages |
| Level | Visibility |
|---|---|
| 0 | Public |
| 1 | Members only |
| 2 | Friends only |
| 3 | Private |
| 4 | Hidden |
Plugins can inject dynamic sections into user profiles. This is the mechanism for adding content like transaction ratings, badges, activity feeds, or any plugin-specific data to a user's profile page.
plugins/{plugin_name}/profile/{plugin_name}.profile.php -> view_profile
plugins/{plugin_name}/profile/{plugin_name}.profile_edit.php -> edit_profile
Both files are optional. A plugin can provide one, both, or neither.
ProfileManager::getProfileExtensions($context) scans plugins/*/profile/ for matching files.
'view' looks for {name}.profile.php'edit' looks for {name}.profile_edit.phpZ_DEPRECATED folder is excludedProfileManager::renderProfileExtensions($user, $cfg, $context) handles inclusion:
getlocale()Every profile extension file receives these variables in scope:
| Variable | Type | Description |
|---|---|---|
$user | array | Full user row from the users table |
$cfg | array | Global configuration |
$profile_context | string | 'view' or 'edit' |
$profile_userid | int | User ID of the profile being displayed |
The plugin's locale array (e.g. $dialmarketplace) is already loaded.
frameheader() / framefooter() in extensions. They render inside an existing frame.return; to exit cleanly if there is nothing to display.?>).exit() or die().SQL::tableExists()).File: plugins/marketplace/profile/marketplace.profile.php
<?php
use Beamreactor\Database\SQL;
// $user, $cfg, $profile_context, $profile_userid available
// $dialmarketplace already loaded
if(!SQL::tableExists('marketplace_ratings')) return;
$count = SQL::queryValue(
'SELECT COUNT(*) FROM marketplace_ratings WHERE seller_id = ?',
[$profile_userid]
);
if($count < 1) return;
$avg = SQL::queryValue(
'SELECT AVG(rating) FROM marketplace_ratings WHERE seller_id = ?',
[$profile_userid]
);
$ratings = SQL::query(
'SELECT rating, comment, buyer_username, created_at
FROM marketplace_ratings
WHERE seller_id = ?
ORDER BY created_at DESC LIMIT 10',
[$profile_userid]
);
echo '<h3>' . ($dialmarketplace[20] ?? 'Transaction Reviews') . '</h3>';
echo '<p>' . round($avg, 1) . '/5 (' . $count . ' ' . ($dialmarketplace[21] ?? 'reviews') . ')</p>';
foreach($ratings as $r)
{
$stars = str_repeat('★', (int)$r['rating']) . str_repeat('☆', 5 - (int)$r['rating']);
echo '<p>' . $stars . ' - ' . htmlspecialchars($r['comment']);
echo ' <small>(' . htmlspecialchars($r['buyer_username']) . ')</small></p>';
}
?>
File: plugins/marketplace/profile/marketplace.profile_edit.php
<?php
use Beamreactor\Database\SQL;
// Only show if user has seller status
$seller = SQL::queryFirst(
'SELECT seller_bio, accept_trades FROM marketplace_sellers WHERE userid = ?',
[$profile_userid]
);
if(!$seller) return;
echo '<div class="form-section">';
echo '<h3>' . ($dialmarketplace[30] ?? 'Seller Settings') . '</h3>';
echo '<div class="form-group">';
echo '<label for="seller_bio">' . ($dialmarketplace[31] ?? 'Seller Bio') . '</label>';
echo '<textarea id="seller_bio" name="marketplace_seller_bio" maxlength="1000">';
echo htmlspecialchars($seller['seller_bio'] ?? '');
echo '</textarea>';
echo '</div>';
echo '</div>';
?>
Profile extensions that add form fields in edit mode must handle their own POST processing. This is typically done in the plugin's main handler (handlers/{name}.mod.php) or by hooking into update_profile.php.