Lecteur Markdown

Knowledge Base › BEAMREACTOR_PROFILE_SYSTEM

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

1. Overview

The profile system handles user profile display and editing through three core files:

FileRole
members/view_profile.phpPublic profile display
members/edit_profile.phpProfile editing (logged-in user)
lib/profilemanager/ProfileManager.class.phpProfile data operations & extension discovery

Profiles are stored in the main users table ($cfg['dbtable']).

2. ProfileManager Class

Namespace: Beamreactor\Members\ProfileManager

Auto-loaded via CoreAutoloader.

Instantiation #

php
use Beamreactor\Members\ProfileManager;
$profileManager = new ProfileManager($cfg);

Core Methods #

MethodReturnsDescription
getProfileBySession(array $session)array|falseFetch profile matching session data
getProfileByUsername(string $username)array|falseFetch profile by username
getProfileById(int $userid)array|falseFetch profile by user ID
updateEmail(int $userid, string $email)boolUpdate email address
updateInfo(int $userid, string $info)boolUpdate bio/info text
updateSkin(int $userid, string $skin)boolChange skin preference
updateLanguage(int $userid, string $language)boolChange language preference
updateNewsletter(int $userid, int $enabled)boolToggle newsletter subscription
updatePrivacy(int $userid, int $privacy)boolSet privacy level (0-4)
updateAvatar(string $username, array $file, array $cfg)arrayUpload and process avatar
validateProfileData(array $data)arrayValidate profile fields
getAvailableSkins()arrayList installed skins
getAvailableLanguages()arrayList available languages

Privacy Levels #

LevelVisibility
0Public
1Members only
2Friends only
3Private
4Hidden

3. Profile Extensions

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.

Convention #

text
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.

Discovery #

ProfileManager::getProfileExtensions($context) scans plugins/*/profile/ for matching files.

  • Context 'view' looks for {name}.profile.php
  • Context 'edit' looks for {name}.profile_edit.php
  • Results are cached (static) — disk scan happens once per request
  • Z_DEPRECATED folder is excluded
  • Extensions are sorted alphabetically by plugin name

Rendering #

ProfileManager::renderProfileExtensions($user, $cfg, $context) handles inclusion:

  1. Iterates over discovered extensions
  2. Preloads each plugin's locale via getlocale()
  3. Includes the extension file

Available Variables #

Every profile extension file receives these variables in scope:

VariableTypeDescription
$userarrayFull user row from the users table
$cfgarrayGlobal configuration
$profile_contextstring'view' or 'edit'
$profile_useridintUser ID of the profile being displayed

The plugin's locale array (e.g. $dialmarketplace) is already loaded.

Rules #

  • No frameheader() / framefooter() in extensions. They render inside an existing frame.
  • Use return; to exit cleanly if there is nothing to display.
  • Always close the PHP tag (?>).
  • No exit() or die().
  • Check table existence before querying (SQL::tableExists()).

Example: Transaction Ratings #

File: plugins/marketplace/profile/marketplace.profile.php

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('&#9733;', (int)$r['rating']) . str_repeat('&#9734;', 5 - (int)$r['rating']);
    echo '<p>' . $stars . ' - ' . htmlspecialchars($r['comment']);
    echo ' <small>(' . htmlspecialchars($r['buyer_username']) . ')</small></p>';
}
?>

Example: Edit Mode Extension #

File: plugins/marketplace/profile/marketplace.profile_edit.php

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>';
?>

4. Integration Points

Where Extensions Render #

  • view_profile.php: After the "About" section, before end of page
  • edit_profile.php: Before the submit button, inside the main form

Processing Edit Data #

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.