/var/www/sultpoint/html/modules/Client/SidebarProvider.php
<?php
/**
* Sidebar provider for Client module account pages.
* Handles: /clientarea/details, /clientarea/contacts, /clientarea/security,
* /clientarea/emails, /account/users, /account/contacts.
*/
namespace Box\Mod\Client;
use Box\Mod\Invoice\Entity\Invoice;
use Box\Mod\Invoice\Repository\InvoiceRepository;
use Box\Mod\Product\Entity\Product;
<<<<<<< HEAD
use Box\Mod\Servicessl\Entity\SslOrder;
=======
>>>>>>> 535bc07df (refactor: implement canonical JPY point-based architecture for pricing, currency conversion, and ledger reconciliation)
use FOSSBilling\SidebarItem;
use FOSSBilling\SidebarProviderInterface;
class SidebarProvider implements SidebarProviderInterface
{
private const ACCOUNT_PAGES = [
'/clientarea/details',
'/clientarea/contacts',
'/clientarea/security',
'/clientarea/emails',
'/clientarea/my-points',
'/account/users',
'/account/contacts',
];
private const USER_PAGES = [
'/user/profile',
'/user/password',
'/user/security',
'/user/accounts',
];
private const SERVICES_PAGES = [
Arguments
"syntax error, unexpected token "<<", expecting end of file"
/var/www/sultpoint/html/library/FOSSBilling/SidebarRegistry.php
* For testing and long-lived processes (Swoole, RoadRunner).
*/
public static function reset(): void
{
self::$providers = [];
}
/**
* Auto-discover and register all module SidebarProviders.
*/
private static function autoRegister(\Pimple\Container $di): void
{
if (!defined('PATH_MODS')) {
return;
}
foreach (glob(PATH_MODS . '/*/SidebarProvider.php') as $file) {
$moduleName = basename(dirname($file));
$className = "Box\\Mod\\{$moduleName}\\SidebarProvider";
if (class_exists($className)) {
$provider = new $className();
if ($provider instanceof SidebarProviderInterface) {
self::register($provider);
} else {
$di['logger']->warning("SidebarProvider in module {$moduleName} does not implement SidebarProviderInterface");
}
}
}
}
private static function fireSidebarBuildEvent(\Pimple\Container $di, ?SidebarItem $sidebar, string $path, string $position): void
{
if ($sidebar === null) {
return;
}
$di['events_manager']->fire([
'event' => 'onAfterSidebarBuild',
'params' => [
'sidebar' => $sidebar,
/var/www/sultpoint/html/library/FOSSBilling/SidebarRegistry.php
* Returns null if no module provides a sidebar for this path.
*/
public static function resolve(\Pimple\Container $di, string $path): ?SidebarItem
{
return self::resolveAll($di, $path)['primary'];
}
/**
* Resolve all sidebars for the current path.
*
* Providers may optionally expose buildSecondary(\Pimple\Container $di, string $path): ?SidebarItem
* to supply a secondary sidebar without forcing every provider to implement it.
*
* @return array{primary: ?SidebarItem, secondary: ?SidebarItem}
*/
public static function resolveAll(\Pimple\Container $di, string $path): array
{
// Lazy auto-registration: only scan for providers when we actually need a sidebar
if (empty(self::$providers)) {
self::autoRegister($di);
}
// 1. Find matching provider
$primarySidebar = null;
$secondarySidebar = null;
foreach (self::$providers as $provider) {
if ($provider->supports($path)) {
$primarySidebar = $provider->build($di);
if (method_exists($provider, 'buildSecondary')) {
$secondarySidebar = $provider->buildSecondary($di, $path);
}
break;
}
}
self::fireSidebarBuildEvent($di, $primarySidebar, $path, 'primary');
self::fireSidebarBuildEvent($di, $secondarySidebar, $path, 'secondary');
/var/www/sultpoint/html/library/Box/AppClient.php
if (!$this->debugBar->hasCollector($collector->getName())) {
$this->debugBar->addCollector($collector);
}
}
$twig->addExtension(new DebugBar($this->getDebugBar()));
if ($this->di['auth']->isClientLoggedIn()) {
$twig->addGlobal('client', $this->di['api_client']);
}
$clientAreaAlertContext = $this->getClientAreaAlertContext();
foreach ($clientAreaAlertContext as $key => $value) {
$twig->addGlobal($key, $value);
}
// Sidebar is 100% opt-in. Ask the provider registry for a sidebar.
// If no module claims this path, an empty sidebar is used (no panels rendered).
// Sidebars exist for both guest-facing pages (Announcements, KB) and logged-in pages.
$currentPath = rtrim((string) (parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/'), '/');
$sidebars = FOSSBilling\SidebarRegistry::resolveAll($this->di, $currentPath);
$twig->addGlobal('primarySidebar', $sidebars['primary'] ?? new FOSSBilling\SidebarItem('primarySidebar', ''));
$twig->addGlobal('secondarySidebar', $sidebars['secondary'] ?? new FOSSBilling\SidebarItem('secondarySidebar', ''));
$isUserLoggedIn = $this->di['auth']->isUserLoggedIn();
$twig->addGlobal('loggedin', $isUserLoggedIn);
if ($isUserLoggedIn) {
$twig->addGlobal('user', $this->di['api_user']);
}
if ($this->di['auth']->isAdminLoggedIn()) {
$twig->addGlobal('admin', $this->di['api_admin']);
$twig->addGlobal('adminLoggedIn', true);
}
// --- Auto Breadcrumb & Page Title ---
$breadcrumbResolver = new FOSSBilling\BreadcrumbResolver();
$breadcrumbResolver->setDi($this->di);
$breadcrumbData = $breadcrumbResolver->resolve($_SERVER['REQUEST_URI'] ?? '/');
$twig->addGlobal('breadcrumbnav', $breadcrumbData['breadcrumbnav']);
$twig->addGlobal('pagetitle', $breadcrumbData['pagetitle']);
/var/www/sultpoint/html/library/Box/AppClient.php
} catch (Exception $e) {
if (DEBUG) {
error_log($e->getMessage());
}
}
$e = new FOSSBilling\InformationException('Page :url not found', [':url' => $this->url], 404);
$this->di['logger']->setChannel('routing')->info($e->getMessage());
http_response_code(404);
return $this->render('error', ['exception' => $e]);
}
/**
* @param string $fileName
*/
public function render($fileName, $variableArray = [], $ext = 'html.twig'): string
{
try {
$template = $this->getTwig()->load(Path::changeExtension($fileName, $ext));
} catch (Twig\Error\LoaderError $e) {
$this->di['logger']->setChannel('routing')->info($e->getMessage());
http_response_code(404);
throw new FOSSBilling\InformationException('Page not found', null, 404);
}
if ("{$fileName}.{$ext}" == 'mod_page_sitemap.xml') {
header('Content-Type: application/xml');
}
// Set filename for WHMCS template compatibility
// Use templatefile if provided, otherwise extract from the template path
if (!isset($variableArray['filename'])) {
$variableArray['filename'] = $variableArray['templatefile'] ?? basename($fileName);
}
return $template->render($variableArray);
}
/var/www/sultpoint/html/library/Box/AppClient.php
// Register User module routes (for /user/* paths — account management)
$userMod = $this->di['mod']('user');
$userMod->registerUserRoutes($this);
// init index module manually
$this->get('', 'get_index');
$this->get('/', 'get_index');
// init custom methods for undefined pages
$this->get('/:page', 'get_custom_page', ['page' => '[a-z0-9-/.//]+']);
$this->post('/:page', 'get_custom_page', ['page' => '[a-z0-9-/.//]+']);
}
}
public function get_index(): string
{
// Use Landingpage service to get homepage data for proper section rendering
$service = $this->di['mod_service']('Landingpage');
return $this->render('landing/homepage', $service->getHomepageData());
}
public function get_custom_page($page): string
{
$ext = $this->ext;
if (str_contains((string) $page, '.')) {
$ext = substr((string) $page, strpos((string) $page, '.') + 1);
$page = substr((string) $page, 0, strpos((string) $page, '.'));
}
$page = str_replace('/', '_', $page);
// Redirect logged-in users away from login/register pages
if (in_array($page, ['login', 'register', 'signup'], true)) {
if ($this->di['auth']->isClientLoggedIn()) {
$redirectUrl = $this->di['url']->link('clientarea');
header("Location: {$redirectUrl}");
exit;
}
}
/var/www/sultpoint/html/library/Box/App.php
}
protected function execute($methodName, $params, $classname = null): string
{
$this->debugBar['time']->startMeasure('execute', 'Reflecting module controller');
$reflection = new ReflectionMethod(static::class, $methodName);
$args = [];
foreach ($reflection->getParameters() as $param) {
if (isset($params[$param->name])) {
$args[$param->name] = $params[$param->name];
} elseif ($param->isDefaultValueAvailable()) {
$args[$param->name] = $param->getDefaultValue();
}
}
$this->debugBar['time']->stopMeasure('execute');
return $reflection->invokeArgs($this, $args);
}
protected function event(string $httpMethod, string $url, string $methodName, ?array $conditions = [], ?string $classname = null): void
{
if (method_exists($this, $methodName)) {
$this->mappings[] = [$httpMethod, $url, $methodName, $conditions];
}
if ($classname !== null) {
$this->shared[] = [$httpMethod, $url, $methodName, $conditions, $classname];
}
}
protected function checkAllowedURLs(): bool
{
$REQUEST_URI = $_SERVER['REQUEST_URI'] ?? null;
$allowedURLs = Config::getProperty('maintenance_mode.allowed_urls', []);
// Allow access to the staff panel all the time
$adminApiPrefixes = [
/var/www/sultpoint/html/library/Box/App.php
$mapping = $this->shared[$i];
$url = new Box_UrlHelper($mapping[0], $mapping[1], $mapping[3], $this->url);
if ($url->match) {
$this->debugBar['time']->stopMeasure('sharedMapping');
return $this->executeShared($mapping[4], $mapping[2], $url->params);
}
}
$this->debugBar['time']->stopMeasure('sharedMapping');
// this class mappings
$this->debugBar['time']->startMeasure('mapping', 'Checking mappings');
$mappingsCount = count($this->mappings);
for ($i = 0; $i < $mappingsCount; ++$i) {
$mapping = $this->mappings[$i];
$url = new Box_UrlHelper($mapping[0], $mapping[1], $mapping[3], $this->url);
if ($url->match) {
$this->debugBar['time']->stopMeasure('mapping');
return $this->execute($mapping[2], $url->params);
}
}
$this->debugBar['time']->stopMeasure('mapping');
$e = new FOSSBilling\InformationException('Page :url not found', [':url' => $this->url], 404);
return $this->show404($e);
}
}
/var/www/sultpoint/html/library/Box/App.php
public function delete(string $url, string $methodName, ?array $conditions = [], ?string $class = null): void
{
$this->event('delete', $url, $methodName, $conditions, $class);
}
public function run(): string
{
$this->debugBar['time']->startMeasure('registerModule', 'Registering module routes');
$this->registerModule();
$this->debugBar['time']->stopMeasure('registerModule');
$this->debugBar['time']->startMeasure('init', 'Initializing the app');
$this->init();
$this->debugBar['time']->stopMeasure('init');
$this->debugBar['time']->startMeasure('checkperm', 'Checking access to module');
$this->checkPermission();
$this->debugBar['time']->stopMeasure('checkperm');
return $this->processRequest();
}
/**
* @param string $path
*/
public function redirect($path): never
{
$location = $this->di['url']->link($path);
header("Location: $location");
exit;
}
public function render($fileName, $variableArray = []): string
{
return 'Rendering ' . $fileName;
}
public function sendFile($filename, $contentType, $path): false|int
{
header("Content-type: $contentType");
/var/www/sultpoint/html/index.php
// If HTTP error code has been passed, handle it.
if (!is_null($http_err_code)) {
switch ($http_err_code) {
case '404':
$e = new FOSSBilling\Exception('Page :url not found', [':url' => $url], 404);
echo $app->show404($e);
break;
default:
$http_err_code = intval($http_err_code);
http_response_code($http_err_code);
$e = new FOSSBilling\Exception('HTTP Error :err_code occurred while attempting to load :url', [':err_code' => $http_err_code, ':url' => $url], $http_err_code);
echo $app->render('error', ['exception' => $e]);
}
exit;
}
// If no HTTP error passed, run the app.
echo $app->run();
exit;