11<?php
22/**
3- * Plugin for Seiger Multisite Tools to Evolution CMS.
3+ * Seiger Multisite Tools plugin for Evolution CMS.
44 *
5- * SSO across multiple domains living in the same Evolution CMS instance:
6- * - After Manager login: propagates the session to other domains.
7- * - After Manager logout: clears the session on other domains.
8- * - Uses short-lived signed tokens (HS256) and a per-login "run plan" (steps).
9- * - Runs in the same tab via location.replace() to avoid popup blockers.
5+ * Provides multisite runtime configuration and Manager UX improvements:
6+ * - Switches Evolution config (site_key/start pages) by the current domain.
7+ * - Namespaces page cache keys per site_key.
8+ * - Rebuilds cached multi-domain tree on cache refresh.
9+ * - Adjusts Manager document URLs to point to the owning domain.
10+ * - Enhances Manager tree with extra domain roots and special node icons.
11+ *
12+ * Also provides optional cross-domain Manager SSO (single sign-on):
13+ * - After Manager login: propagates the session SID to other domains.
14+ * - After Manager logout: clears the session SID on other domains.
15+ * - Uses short-lived signed tokens (HS256) and a per-run "plan" (steps).
16+ * - Executes in the same tab via location.replace() to avoid popup blockers.
17+ *
18+ * Endpoints (friendly_url_suffix is respected):
19+ * - /_ms-run, /_ms-run-logout Runner endpoints (step sequencer)
20+ * - /_ms-sso, /_ms-sso-logout Receiver endpoints (set/unset SID)
1021 */
1122
1223use EvolutionCMS \Facades \UrlProcessor ;
1728
1829// Load SSO functions if needed
1930if (!function_exists ('ms_sso_load_functions ' )) {
31+ /**
32+ * Load SSO helper functions once.
33+ *
34+ * The helpers are expected at: ../functions/sso.php
35+ * - ms_sso_token_make()
36+ * - ms_sso_token_parse()
37+ * - ms_run_put(), ms_run_get(), ms_run_touch(), ms_run_del()
38+ *
39+ * @return void
40+ */
2041 function ms_sso_load_functions (): void {
2142 static $ loaded = false ;
2243 if (!$ loaded ) {
@@ -30,8 +51,18 @@ function ms_sso_load_functions(): void {
3051}
3152
3253/**
33- * OnLoadSettings:
34- * Switch Evolution config by domain (site_key, start pages), and hydrate documentListing from cache.
54+ * OnLoadSettings
55+ *
56+ * Switches Evolution configuration by domain:
57+ * - site_key, site_root, site_name
58+ * - site_start, error_page, unauthorized_page
59+ * - site_color
60+ *
61+ * Also hydrates UrlProcessor::documentListing from cache for the selected site_key.
62+ *
63+ * @param array $params Event payload (may include ['config']['setHost'] override)
64+ *
65+ * @return void
3566 */
3667Event::listen ('evolution.OnLoadSettings ' , function ($ params ) {
3768 $ host = $ _SERVER ['HTTP_HOST ' ];
@@ -58,9 +89,13 @@ function ms_sso_load_functions(): void {
5889});
5990
6091/**
61- * OnWebPageInit:
62- * Enforce resource access per domain on the front-end.
63- * Whitelist SSO endpoints to avoid accidental 404.
92+ * OnWebPageInit
93+ *
94+ * Enforces resource access per domain on the front-end:
95+ * - Compares evo()->documentIdentifier against cached allowed resource IDs for the current site_key.
96+ * - Whitelists SSO service endpoints to avoid accidental 404 / access blocks.
97+ *
98+ * @return void
6499 */
65100Event::listen ('evolution.OnWebPageInit ' , function () {
66101 $ uri = strtok ($ _SERVER ['REQUEST_URI ' ] ?? '/ ' , '? ' );
@@ -76,8 +111,16 @@ function ms_sso_load_functions(): void {
76111});
77112
78113/**
79- * OnMakeDocUrl:
80- * For Manager links, prefix URLs with the appropriate domain.
114+ * OnMakeDocUrl
115+ *
116+ * For Manager links, prefixes document URLs with the domain that owns the resource root.
117+ * This allows correct cross-domain navigation from Manager UI.
118+ *
119+ * @param array $params Event payload:
120+ * - int $params['id'] Document ID
121+ * - string $params['url'] Generated (relative) URL
122+ *
123+ * @return string|null Prefixed URL for Manager context, otherwise null to keep default behavior
81124 */
82125Event::listen ('evolution.OnMakeDocUrl ' , function ($ params ) {
83126 if (evo ()->isBackend ()) {
@@ -100,33 +143,57 @@ function ms_sso_load_functions(): void {
100143});
101144
102145/**
103- * OnMakePageCacheKey:
104- * Namespaces page cache by site_key.
146+ * OnMakePageCacheKey
147+ *
148+ * Namespaces page cache keys by site_key to prevent collisions across domains.
149+ *
150+ * @param array $params Event payload:
151+ * - string $params['hash'] Base cache key hash
152+ *
153+ * @return string
105154 */
106155Event::listen ('evolution.OnMakePageCacheKey ' , function ($ params ) {
107156 return evo ()->getConfig ('site_key ' , 'default ' ) . '_ ' . $ params ['hash ' ];
108157});
109158
110159/**
111- * OnCacheUpdate:
112- * Rebuild cached multi-domain tree.
160+ * OnCacheUpdate
161+ *
162+ * Rebuilds the cached multi-domain tree/listings.
163+ *
164+ * @param array $params Event payload (unused)
165+ *
166+ * @return void
113167 */
114168Event::listen ('evolution.OnCacheUpdate ' , function ($ params ) {
115169 sMultisite::domainsTree ();
116170});
117171
118172/**
119- * OnDocFormPrerender:
120- * Ensure System Settings panel loads values from the domain the document belongs to.
173+ * OnDocFormPrerender
174+ *
175+ * Ensures the System Settings panel loads values for the domain the document belongs to.
176+ * It resolves a host for the given document ID and triggers OnLoadSettings with setHost override.
177+ *
178+ * @param array $params Event payload:
179+ * - int|string $params['id'] Document ID being edited
180+ *
181+ * @return void
121182 */
122183Event::listen ('evolution.OnDocFormPrerender ' , function ($ params ) {
123184 $ config = array_merge (evo ()->config , ['setHost ' => parse_url (url ($ params ['id ' ]), PHP_URL_HOST )]);
124185 evo ()->invokeEvent ('OnLoadSettings ' , ['config ' => &$ config ]);
125186});
126187
127188/**
128- * OnManagerMenuPrerender:
129- * Adds “sMultisite” entry to Tools (for users with 'settings' permission).
189+ * OnManagerMenuPrerender
190+ *
191+ * Adds “sMultisite” entry to Tools for users with 'settings' permission.
192+ *
193+ * @param array $params Event payload:
194+ * - array $params['menu'] Current Manager menu structure
195+ *
196+ * @return string|null Serialized updated menu, or null to keep default behavior
130197 */
131198Event::listen ('evolution.OnManagerMenuPrerender ' , function ($ params ) {
132199 if (evo ()->hasPermission ('settings ' )) {
@@ -148,9 +215,16 @@ function ms_sso_load_functions(): void {
148215});
149216
150217/**
151- * OnManagerLogin:
218+ * OnManagerLogin
219+ *
152220 * After successful Manager login, builds a "run plan" to authenticate on other domains.
153- * Stores runId in session; the plan is executed from OnManagerWelcomeHome.
221+ * Stores runId in PHP session; the plan is executed from evolution.OnManagerWelcomeHome.
222+ *
223+ * Notes:
224+ * - Uses current session_id() as SID value to propagate.
225+ * - Tokens are short-lived and include mode/login, sid, and host.
226+ *
227+ * @return void
154228 */
155229Event::listen ('evolution.OnManagerLogin ' , function () {
156230 ms_sso_load_functions ();
@@ -196,9 +270,16 @@ function ms_sso_load_functions(): void {
196270});
197271
198272/**
199- * OnManagerLogout:
273+ * OnManagerLogout
274+ *
200275 * Builds a "run plan" to logout on other domains.
201- * Stores runId in a cookie so we can start from OnManagerWelcomeHome.
276+ * Stores runId in a cookie so we can start from evolution.OnManagerWelcomeHome (session may be gone).
277+ *
278+ * Notes:
279+ * - Tokens are short-lived and include mode/logout and host.
280+ * - Cookie is intentionally not HttpOnly to allow JS-less redirections.
281+ *
282+ * @return void
202283 */
203284Event::listen ('evolution.OnManagerLogout ' , function () {
204285 ms_sso_load_functions ();
@@ -237,9 +318,12 @@ function ms_sso_load_functions(): void {
237318});
238319
239320/**
240- * OnManagerWelcomeHome:
321+ * OnManagerWelcomeHome
322+ *
241323 * Kicks off the run (login/logout) in the same tab using location.replace().
242- * We pass "ret" (final Manager URL) so the flow comes back automatically.
324+ * Passes "ret" (final Manager URL) so the flow returns automatically.
325+ *
326+ * @return void
243327 */
244328Event::listen ('evolution.OnManagerWelcomeHome ' , function () {
245329 ms_sso_load_functions ();
@@ -296,11 +380,17 @@ function ms_sso_load_functions(): void {
296380});
297381
298382/**
299- * OnBeforeLoadDocumentObject:
383+ * OnBeforeLoadDocumentObject
384+ *
300385 * Implements service endpoints:
301- * - /_ms-run, /_ms-run-logout → runners (sequence executor across domains)
302- * - /_ms-sso, /_ms-sso-logout → receivers (set/unset SID and bounce to next/ret)
303- * Has <meta refresh> fallback and blocks preloads to avoid consuming tokens prematurely.
386+ * - /_ms-run, /_ms-run-logout Runner endpoints (sequence executor across domains)
387+ * - /_ms-sso, /_ms-sso-logout Receiver endpoints (set/unset SID and bounce to next/ret)
388+ *
389+ * Security/behavior:
390+ * - Has <meta refresh> fallback for no-JS.
391+ * - Blocks speculative preloads/prerenders to avoid consuming one-shot tokens prematurely.
392+ *
393+ * @return void
304394 */
305395Event::listen ('evolution.OnBeforeLoadDocumentObject ' , function () {
306396 $ uri = strtok ($ _SERVER ['REQUEST_URI ' ] ?? '/ ' , '? ' );
@@ -503,8 +593,37 @@ function ms_sso_load_functions(): void {
503593});
504594
505595/**
506- * OnManagerTreeRender:
507- * Renders extra root nodes for each domain in the Resources tree.
596+ * OnManagerTreePrerender
597+ *
598+ * Forces Manager tree context to the "default" domain settings.
599+ * This avoids Manager tree inconsistencies when switching documents/domains.
600+ *
601+ * @param array $params Event payload (unused)
602+ *
603+ * @return void
604+ */
605+ Event::listen ('evolution.OnManagerTreePrerender ' , function ($ params ) {
606+ $ domain = \Seiger \sMultisite \Models \sMultisite::where ('key ' , 'default ' )->first ();
607+ if ($ domain ) {
608+ evo ()->setConfig ('site_key ' , $ domain ->key );
609+ evo ()->setConfig ('site_name ' , $ domain ->site_name );
610+ evo ()->setConfig ('site_start ' , $ domain ->site_start );
611+ evo ()->setConfig ('error_page ' , $ domain ->error_page );
612+ evo ()->setConfig ('unauthorized_page ' , $ domain ->unauthorized_page );
613+ evo ()->setConfig ('site_root ' , (int )$ domain ->resource );
614+ evo ()->setConfig ('site_color ' , $ domain ->site_color );
615+ }
616+ });
617+
618+ /**
619+ * OnManagerTreeRender
620+ *
621+ * Renders extra root nodes for each domain in the Resources tree (except default).
622+ * Each domain is rendered as a "rootNode" container with its own treeRoot placeholder.
623+ *
624+ * @param array $params Event payload (unused)
625+ *
626+ * @return string|null HTML markup for extra roots, or null to keep default behavior
508627 */
509628Event::listen ('evolution.OnManagerTreeRender ' , function ($ params ) {
510629 $ tree = '' ;
@@ -528,11 +647,22 @@ function ms_sso_load_functions(): void {
528647});
529648
530649/**
531- * OnManagerNodePrerender:
532- * Sets special icons and disables move for domain's key pages (home/error/unauthorized).
650+ * OnManagerNodePrerender
651+ *
652+ * Sets special icons and disables move for domain key pages:
653+ * - home (site_start)
654+ * - error_page
655+ * - unauthorized_page
656+ *
657+ * Also optionally marks sCommerce catalog root categories if enabled (check_sCommerce).
658+ *
659+ * @param array $params Event payload:
660+ * - array $params['ph'] Placeholder array for a node (must be returned serialized)
661+ *
662+ * @return string|null Serialized placeholders, or null to keep default behavior
533663 */
534664Event::listen ('evolution.OnManagerNodePrerender ' , function ($ params ) {
535- $ domains = \Seiger \sMultisite \Models \sMultisite::where ('hide_from_tree ' , 0 )->whereNot ( ' key ' , ' default ' )-> get ();
665+ $ domains = \Seiger \sMultisite \Models \sMultisite::where ('hide_from_tree ' , 0 )->get ();
536666 if ($ domains ) {
537667 $ _style = ManagerTheme::getStyle ();
538668 $ startResources = $ domains ->pluck ('site_start ' )->toArray ();
@@ -575,8 +705,15 @@ function ms_sso_load_functions(): void {
575705});
576706
577707/**
578- * OnManagerNodeRender:
579- * Hides root resources that act as domain containers from the tree.
708+ * OnManagerNodeRender
709+ *
710+ * Hides domain container root resources from the tree.
711+ * If the current node ID is one of sMultisite domain container resources, returns a blank string.
712+ *
713+ * @param array $params Event payload:
714+ * - int $params['id'] Node document ID
715+ *
716+ * @return string|null Blank string to hide, or null to keep default behavior
580717 */
581718Event::listen ('evolution.OnManagerNodeRender ' , function ($ params ) {
582719 $ domains = \Seiger \sMultisite \Models \sMultisite::all ();
0 commit comments