@@ -1388,6 +1388,9 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
13881388 $ started = 0 ;
13891389 $ paused = 0 ;
13901390 $ stopped = 0 ;
1391+ $ folderManagerTypes = [];
1392+ $ folderManagedCount = 0 ;
1393+ $ folderUpToDate = true ;
13911394 foreach ($ members as $ name ) {
13921395 $ item = $ infoByName [$ name ] ?? null ;
13931396 if (!is_array ($ item )) {
@@ -1404,6 +1407,19 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
14041407 } else {
14051408 $ stopped ++;
14061409 }
1410+ if ($ type === 'docker ' ) {
1411+ $ memberManager = trim ((string )($ item ['info ' ]['State ' ]['manager ' ] ?? ($ item ['manager ' ] ?? '' )));
1412+ if ($ memberManager !== '' ) {
1413+ $ folderManagerTypes [$ memberManager ] = true ;
1414+ }
1415+ if ($ memberManager === 'dockerman ' ) {
1416+ $ folderManagedCount ++;
1417+ $ memberUpdated = $ item ['info ' ]['State ' ]['Updated ' ] ?? ($ item ['Updated ' ] ?? null );
1418+ if ($ memberUpdated === false ) {
1419+ $ folderUpToDate = false ;
1420+ }
1421+ }
1422+ }
14071423 }
14081424
14091425 $ total = count ($ members );
@@ -1423,6 +1439,8 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
14231439 } elseif ($ statusKind === 'stopped ' ) {
14241440 $ badgeVisible = $ showStoppedBadge ;
14251441 }
1442+ $ hideUpdateColumn = diagnosticsFolderSettingBool (is_array ($ folder ) ? $ folder : [], 'update_column ' , false );
1443+ $ previewUpdate = diagnosticsFolderSettingBool (is_array ($ folder ) ? $ folder : [], 'preview_update ' , false );
14261444
14271445 $ folderStatusTotals [$ statusKind ]++;
14281446 $ memberTotals ['started ' ] += $ started ;
@@ -1448,8 +1466,21 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
14481466 'text ' => $ statusText ,
14491467 'badgeVisible ' => $ badgeVisible ,
14501468 'colors ' => diagnosticsFolderStatusColors (is_array ($ folder ) ? $ folder : [])
1469+ ],
1470+ 'settings ' => [
1471+ 'previewUpdate ' => $ previewUpdate ,
1472+ 'hideUpdateColumn ' => $ hideUpdateColumn
14511473 ]
14521474 ];
1475+ if ($ type === 'docker ' ) {
1476+ $ snapshotFolders [$ safeFolderId ]['renderExpectations ' ] = diagnosticsDockerFolderRenderExpectations (
1477+ array_keys ($ folderManagerTypes ),
1478+ $ folderUpToDate ,
1479+ $ folderManagedCount ,
1480+ $ showUpdateBadge ,
1481+ $ hideUpdateColumn
1482+ );
1483+ }
14531484 }
14541485
14551486 $ entityDetails = [];
@@ -1477,6 +1508,22 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
14771508 if ($ entityDetailsTotal > $ entityDetailsMaxEntries ) {
14781509 continue ;
14791510 }
1511+ $ provenance = [
1512+ 'managerSource ' => 'missing ' ,
1513+ 'updateSource ' => 'missing '
1514+ ];
1515+ $ renderExpectations = [
1516+ 'statusToken ' => $ updated === true ? 'upToDate ' : ($ updated === false ? 'available ' : 'unknown ' ),
1517+ 'action ' => 'none ' ,
1518+ 'forceUpdateEligible ' => false
1519+ ];
1520+ if ($ type === 'docker ' ) {
1521+ $ provenance = [
1522+ 'managerSource ' => diagnosticsDockerStateFieldSource ($ item , 'manager ' ),
1523+ 'updateSource ' => diagnosticsDockerStateFieldSource ($ item , 'updated ' )
1524+ ];
1525+ $ renderExpectations = diagnosticsDockerMemberRenderExpectations ($ manager !== '' ? $ manager : null , $ updated );
1526+ }
14801527 $ entityDetails [] = [
14811528 'name ' => normalizeDiagnosticsPrivacyMode ($ privacyMode ) === 'full ' ? (string )$ name : null ,
14821529 'nameHash ' => diagnosticsHashShort ((string )$ name ),
@@ -1485,7 +1532,9 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
14851532 'manager ' => $ manager !== '' ? $ manager : null ,
14861533 'managed ' => $ manager === 'dockerman ' ,
14871534 'updated ' => $ updated ,
1488- 'updateState ' => $ updated === true ? 'upToDate ' : ($ updated === false ? 'available ' : 'unknown ' )
1535+ 'updateState ' => $ updated === true ? 'upToDate ' : ($ updated === false ? 'available ' : 'unknown ' ),
1536+ 'provenance ' => $ provenance ,
1537+ 'renderExpectations ' => $ renderExpectations
14891538 ];
14901539 }
14911540 if (!empty ($ managerCounts )) {
@@ -1633,6 +1682,105 @@ function diagnosticsBuildIntegrityIssueDetail(array $integrity): string {
16331682 return '' ;
16341683 }
16351684
1685+ function diagnosticsFolderSettingBool (array $ folder , string $ key , bool $ default = false ): bool {
1686+ $ settings = is_array ($ folder ['settings ' ] ?? null ) ? $ folder ['settings ' ] : [];
1687+ if (!array_key_exists ($ key , $ settings )) {
1688+ return $ default ;
1689+ }
1690+ return normalizeBool ($ settings [$ key ], $ default );
1691+ }
1692+
1693+ function diagnosticsDockerStateFieldSource (array $ item , string $ field ): string {
1694+ $ state = is_array ($ item ['info ' ]['State ' ] ?? null ) ? $ item ['info ' ]['State ' ] : [];
1695+ if ($ field === 'manager ' ) {
1696+ if (trim ((string )($ state ['manager ' ] ?? '' )) !== '' ) {
1697+ return 'infoState ' ;
1698+ }
1699+ if (trim ((string )($ item ['manager ' ] ?? '' )) !== '' ) {
1700+ return 'topLevelFallback ' ;
1701+ }
1702+ return 'missing ' ;
1703+ }
1704+ if ($ field === 'updated ' ) {
1705+ if (is_bool ($ state ['Updated ' ] ?? null )) {
1706+ return 'infoState ' ;
1707+ }
1708+ if (is_bool ($ item ['Updated ' ] ?? null )) {
1709+ return 'topLevelFallback ' ;
1710+ }
1711+ return 'missing ' ;
1712+ }
1713+ return 'missing ' ;
1714+ }
1715+
1716+ function diagnosticsDockerMemberRenderExpectations (?string $ manager , ?bool $ updated ): array {
1717+ $ safeManager = trim ((string )($ manager ?? '' ));
1718+ if ($ safeManager === 'composeman ' ) {
1719+ return [
1720+ 'statusToken ' => 'compose ' ,
1721+ 'action ' => 'none ' ,
1722+ 'forceUpdateEligible ' => false
1723+ ];
1724+ }
1725+ if ($ safeManager !== '' && $ safeManager !== 'dockerman ' ) {
1726+ return [
1727+ 'statusToken ' => 'thirdParty ' ,
1728+ 'action ' => 'none ' ,
1729+ 'forceUpdateEligible ' => false
1730+ ];
1731+ }
1732+ if ($ safeManager === 'dockerman ' && $ updated === false ) {
1733+ return [
1734+ 'statusToken ' => 'updateReady ' ,
1735+ 'action ' => 'applyUpdate ' ,
1736+ 'forceUpdateEligible ' => false
1737+ ];
1738+ }
1739+ return [
1740+ 'statusToken ' => 'upToDate ' ,
1741+ 'action ' => 'forceUpdate ' ,
1742+ 'forceUpdateEligible ' => true
1743+ ];
1744+ }
1745+
1746+ function diagnosticsDockerFolderRenderExpectations (array $ managerTypes , bool $ upToDate , int $ managedCount , bool $ showUpdateBadge , bool $ hideUpdateColumn ): array {
1747+ $ safeManagerTypes = array_values (array_filter (array_map ('strval ' , $ managerTypes ), static function ($ value ): bool {
1748+ return trim ($ value ) !== '' ;
1749+ }));
1750+ $ safeManagerTypes = array_values (array_unique ($ safeManagerTypes ));
1751+ sort ($ safeManagerTypes );
1752+
1753+ $ hasDockerMan = in_array ('dockerman ' , $ safeManagerTypes , true );
1754+ $ hasCompose = in_array ('composeman ' , $ safeManagerTypes , true );
1755+ $ hasThirdParty = count (array_filter ($ safeManagerTypes , static function ($ value ): bool {
1756+ return $ value !== 'dockerman ' && $ value !== 'composeman ' ;
1757+ })) > 0 ;
1758+
1759+ $ statusToken = 'upToDate ' ;
1760+ $ action = 'none ' ;
1761+ if (!$ hasDockerMan && $ hasCompose && $ hasThirdParty ) {
1762+ $ statusToken = 'composeAndThirdParty ' ;
1763+ } elseif (!$ hasDockerMan && $ hasCompose ) {
1764+ $ statusToken = 'compose ' ;
1765+ } elseif (!$ hasDockerMan ) {
1766+ $ statusToken = 'thirdParty ' ;
1767+ } elseif (!$ upToDate ) {
1768+ $ statusToken = 'updateReady ' ;
1769+ $ action = 'applyUpdate ' ;
1770+ } elseif ($ managedCount > 0 ) {
1771+ $ action = 'forceUpdate ' ;
1772+ }
1773+
1774+ return [
1775+ 'updateColumnVisible ' => $ showUpdateBadge && !$ hideUpdateColumn ,
1776+ 'statusToken ' => $ statusToken ,
1777+ 'action ' => $ action ,
1778+ 'actionRequiresAdvancedView ' => in_array ($ action , ['applyUpdate ' , 'forceUpdate ' ], true ),
1779+ 'forceUpdateEligible ' => $ action === 'forceUpdate ' ,
1780+ 'managerTypes ' => $ safeManagerTypes
1781+ ];
1782+ }
1783+
16361784 function diagnosticsBuildRecommendedActions (array $ typesData , array $ customIcons ): array {
16371785 $ actions = [];
16381786 $ addAction = static function (string $ action , string $ label , string $ reason ) use (&$ actions ): void {
@@ -2201,7 +2349,24 @@ function diagnosticsBuildSupportBundleRuntimeEntityDetails(string $type, array $
22012349 'updated ' => $ updated ,
22022350 'updateState ' => in_array ((string )($ entry ['updateState ' ] ?? '' ), ['available ' , 'upToDate ' , 'unknown ' ], true )
22032351 ? (string )$ entry ['updateState ' ]
2204- : 'unknown '
2352+ : 'unknown ' ,
2353+ 'provenance ' => [
2354+ 'managerSource ' => in_array ((string )($ entry ['provenance ' ]['managerSource ' ] ?? '' ), ['infoState ' , 'topLevelFallback ' , 'missing ' ], true )
2355+ ? (string )$ entry ['provenance ' ]['managerSource ' ]
2356+ : 'missing ' ,
2357+ 'updateSource ' => in_array ((string )($ entry ['provenance ' ]['updateSource ' ] ?? '' ), ['infoState ' , 'topLevelFallback ' , 'missing ' ], true )
2358+ ? (string )$ entry ['provenance ' ]['updateSource ' ]
2359+ : 'missing '
2360+ ],
2361+ 'renderExpectations ' => [
2362+ 'statusToken ' => in_array ((string )($ entry ['renderExpectations ' ]['statusToken ' ] ?? '' ), ['compose ' , 'thirdParty ' , 'updateReady ' , 'upToDate ' , 'available ' , 'unknown ' ], true )
2363+ ? (string )$ entry ['renderExpectations ' ]['statusToken ' ]
2364+ : 'unknown ' ,
2365+ 'action ' => in_array ((string )($ entry ['renderExpectations ' ]['action ' ] ?? '' ), ['none ' , 'applyUpdate ' , 'forceUpdate ' ], true )
2366+ ? (string )$ entry ['renderExpectations ' ]['action ' ]
2367+ : 'none ' ,
2368+ 'forceUpdateEligible ' => (bool )($ entry ['renderExpectations ' ]['forceUpdateEligible ' ] ?? false )
2369+ ]
22052370 ];
22062371 }
22072372 if ((bool )($ details ['truncated ' ] ?? false )) {
@@ -2235,6 +2400,26 @@ function diagnosticsBuildSupportBundleRuntimeTypeSection(string $type, array $ty
22352400 if (!isset ($ folder ['members ' ]) || !is_array ($ folder ['members ' ])) {
22362401 $ folder ['members ' ] = [];
22372402 }
2403+ $ folder ['settings ' ] = [
2404+ 'previewUpdate ' => (bool )($ folder ['settings ' ]['previewUpdate ' ] ?? false ),
2405+ 'hideUpdateColumn ' => (bool )($ folder ['settings ' ]['hideUpdateColumn ' ] ?? false )
2406+ ];
2407+ if ($ type === 'docker ' ) {
2408+ $ folder ['renderExpectations ' ] = [
2409+ 'updateColumnVisible ' => (bool )($ folder ['renderExpectations ' ]['updateColumnVisible ' ] ?? false ),
2410+ 'statusToken ' => in_array ((string )($ folder ['renderExpectations ' ]['statusToken ' ] ?? '' ), ['compose ' , 'composeAndThirdParty ' , 'thirdParty ' , 'updateReady ' , 'upToDate ' ], true )
2411+ ? (string )$ folder ['renderExpectations ' ]['statusToken ' ]
2412+ : 'upToDate ' ,
2413+ 'action ' => in_array ((string )($ folder ['renderExpectations ' ]['action ' ] ?? '' ), ['none ' , 'applyUpdate ' , 'forceUpdate ' ], true )
2414+ ? (string )$ folder ['renderExpectations ' ]['action ' ]
2415+ : 'none ' ,
2416+ 'actionRequiresAdvancedView ' => (bool )($ folder ['renderExpectations ' ]['actionRequiresAdvancedView ' ] ?? false ),
2417+ 'forceUpdateEligible ' => (bool )($ folder ['renderExpectations ' ]['forceUpdateEligible ' ] ?? false ),
2418+ 'managerTypes ' => array_values (array_filter (array_map ('strval ' , is_array ($ folder ['renderExpectations ' ]['managerTypes ' ] ?? null ) ? $ folder ['renderExpectations ' ]['managerTypes ' ] : []), static function ($ value ): bool {
2419+ return trim ($ value ) !== '' ;
2420+ }))
2421+ ];
2422+ }
22382423 if (($ redactor ['mode ' ] ?? FVPLUS_DIAGNOSTICS_DEFAULT_PRIVACY ) === 'full ' ) {
22392424 $ folder ['members ' ]['items ' ] = array_slice (array_map ('strval ' , $ memberItems ), 0 , 40 );
22402425 } else {
@@ -2254,7 +2439,8 @@ static function ($name) use (&$redactor, $fieldPath): ?string {
22542439 $ prefsPath = (string )($ typeData ['prefsPath ' ] ?? '' );
22552440
22562441 return [
2257- 'hostPageDetected ' => true ,
2442+ 'runtimeSnapshotAvailable ' => !empty ($ stateSnapshot ),
2443+ 'snapshotSource ' => 'serverDiagnostics ' ,
22582444 'entitySummary ' => [
22592445 'total ' => (int )($ stateSnapshot ['totalItems ' ] ?? 0 ),
22602446 'assigned ' => (int )($ stateSnapshot ['assignedItems ' ] ?? 0 ),
0 commit comments