mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 00:55:07 +00:00
334b11d562
Two seams, in the shape docs/extension-points-architecture.md settles on: a Laravel event with a mutable payload, dispatched unconditionally, and with nothing listening the documented default holds. A community installation gets an empty list and a null callout, which is exactly what it had before. ResolvingNavigationLinks exists because the sidebar is a hardcoded array in app-sidebar.tsx, so a package could not contribute to it at all — the nav entry was a separate manual edit every time a package grew a screen, and being manual it was forgotten more than once. Staff-only, decided in HandleInertiaRequests rather than trusted to each listener: these render in the administration area, and a client's portal shows their own files and nothing about the installation. There is a test that a listener adding unconditionally still reaches no client. ResolvingDashboardCallout is one band above the widget grid rather than a widget in it. The grid is a closed list of keys that dashboard.tsx renders one by one and each viewer arranges, so a message that mattered would sit wherever somebody dragged it, or under a fold, or switched off. One at a time, first listener wins: a dashboard that can accumulate banners accumulates them, and the second is what teaches people to skip the first. Core learns nothing about what either seam carries. Titles, URLs and copy all arrive from the listener, and that is not fastidiousness — the first caller is the hosted edition's link to its own customer portal and its pitch to free instances, which is commercial copy belonging to one offering and has no business sitting in the public repository because the sidebar happens to live here. An external link renders as a plain anchor opening in a new tab, never an Inertia <Link>: Link expects a page component back and another origin will not give it one, so it fails without saying so. It is also never marked active — nothing outside this app is the page you are on.
57 lines
2.1 KiB
PHP
57 lines
2.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Platform\Navigation\Events;
|
|
|
|
/**
|
|
* Extra links a package wants in the sidebar.
|
|
*
|
|
* The sidebar is built from a hardcoded array in app-sidebar.tsx, which
|
|
* means a package could not contribute to it at all — the nav link was a
|
|
* separate manual edit every time a package grew a screen, and being
|
|
* manual it was forgotten. This is the seam that fixes that, in the shape
|
|
* the extension-points document settles on: core dispatches
|
|
* unconditionally, listeners add or do not, and with no listener the
|
|
* default (no extra links) holds.
|
|
*
|
|
* **Core deliberately learns nothing about what is added.** A link's
|
|
* label, its URL and the reason it exists all arrive from whoever
|
|
* registers it. That is not fastidiousness: the first caller is the
|
|
* hosted edition's link to its own customer portal, and a product URL
|
|
* belonging to one commercial offering has no business sitting in the
|
|
* public repository just because the sidebar happens to live here.
|
|
*
|
|
* Staff only, and enforced here rather than trusted to each listener:
|
|
* these appear in the administration area, and a client's portal shows
|
|
* only their own files.
|
|
*/
|
|
class ResolvingNavigationLinks
|
|
{
|
|
/**
|
|
* @var list<array{title: string, url: string, external: bool, icon: string|null}>
|
|
*/
|
|
public array $links = [];
|
|
|
|
public function __construct(
|
|
/** Whether the viewer is a staff account. Listeners that only make
|
|
* sense for staff should check this rather than assume. */
|
|
public readonly bool $isStaff,
|
|
) {}
|
|
|
|
/**
|
|
* `external` opens in a new tab and marks the link as leaving this
|
|
* installation — a link that navigates a person away from the app
|
|
* they are working in should say so before they click it, not after.
|
|
*/
|
|
public function add(string $title, string $url, bool $external = false, ?string $icon = null): void
|
|
{
|
|
$this->links[] = [
|
|
'title' => $title,
|
|
'url' => $url,
|
|
'external' => $external,
|
|
'icon' => $icon,
|
|
];
|
|
}
|
|
}
|