@aglyn/plugins-marketplace 1.19.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +15 -15
- package/src/lib/components/listing-content.component.d.ts +5 -5
- package/src/lib/components/listing-content.component.js +87 -70
- package/src/lib/components/listing-content.component.js.map +1 -1
- package/src/lib/components/listing-detail-page.component.d.ts +2 -4
- package/src/lib/components/listing-detail-page.component.js.map +1 -1
- package/src/lib/components/marketplace-hub.component.js +10 -3
- package/src/lib/components/marketplace-hub.component.js.map +1 -1
- package/src/lib/components/plugin-site-set-panel.component.d.ts +7 -4
- package/src/lib/components/plugin-site-set-panel.component.js +3 -2
- package/src/lib/components/plugin-site-set-panel.component.js.map +1 -1
- package/src/lib/components/plugin-site-set.component.js +11 -0
- package/src/lib/components/plugin-site-set.component.js.map +1 -1
- package/src/lib/hooks/use-marketplace-actions.js +14 -1
- package/src/lib/hooks/use-marketplace-actions.js.map +1 -1
- package/src/lib/model/marketplace-catalog.d.ts +25 -4
- package/src/lib/model/marketplace-catalog.js +31 -6
- package/src/lib/model/marketplace-catalog.js.map +1 -1
- package/src/lib/model/marketplace.d.ts +28 -2
- package/src/lib/model/marketplace.js +36 -0
- package/src/lib/model/marketplace.js.map +1 -1
- package/src/lib/server/install-plugin.js +52 -8
- package/src/lib/server/install-plugin.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-marketplace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,20 +25,20 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.
|
|
29
|
-
"@aglyn/aglyn-markdown-editor": "1.
|
|
30
|
-
"@aglyn/shared-data-enums": "1.
|
|
31
|
-
"@aglyn/shared-data-mdi": "1.
|
|
32
|
-
"@aglyn/shared-ui-jsx": "1.
|
|
33
|
-
"@aglyn/shared-ui-next": "1.
|
|
34
|
-
"@aglyn/shared-ui-snackstack": "1.
|
|
35
|
-
"@aglyn/shared-util-email": "1.
|
|
36
|
-
"@aglyn/shared-util-http": "1.
|
|
37
|
-
"@aglyn/shared-util-timestamp": "1.
|
|
38
|
-
"@aglyn/shared-util-tools": "1.
|
|
39
|
-
"@aglyn/tenant-data-admin": "1.
|
|
40
|
-
"@aglyn/tenant-feature-instance": "1.
|
|
41
|
-
"@aglyn/tenant-runtime": "1.
|
|
28
|
+
"@aglyn/aglyn": "1.20.0",
|
|
29
|
+
"@aglyn/aglyn-markdown-editor": "1.20.0",
|
|
30
|
+
"@aglyn/shared-data-enums": "1.20.0",
|
|
31
|
+
"@aglyn/shared-data-mdi": "1.20.0",
|
|
32
|
+
"@aglyn/shared-ui-jsx": "1.20.0",
|
|
33
|
+
"@aglyn/shared-ui-next": "1.20.0",
|
|
34
|
+
"@aglyn/shared-ui-snackstack": "1.20.0",
|
|
35
|
+
"@aglyn/shared-util-email": "1.20.0",
|
|
36
|
+
"@aglyn/shared-util-http": "1.20.0",
|
|
37
|
+
"@aglyn/shared-util-timestamp": "1.20.0",
|
|
38
|
+
"@aglyn/shared-util-tools": "1.20.0",
|
|
39
|
+
"@aglyn/tenant-data-admin": "1.20.0",
|
|
40
|
+
"@aglyn/tenant-feature-instance": "1.20.0",
|
|
41
|
+
"@aglyn/tenant-runtime": "1.20.0",
|
|
42
42
|
"@swc/helpers": "0.5.23"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OrgInstallHost } from '../model/marketplace';
|
|
1
2
|
export declare function ListingReadme({ readme }: {
|
|
2
3
|
readme: string;
|
|
3
4
|
}): import("react").JSX.Element;
|
|
@@ -41,12 +42,11 @@ export interface MarketplaceListingContentProps {
|
|
|
41
42
|
/**
|
|
42
43
|
* The org's sites, for the install-targeting picker (AGL-773). Present only
|
|
43
44
|
* at org scope; when given (and non-empty) the CTA offers All sites vs
|
|
44
|
-
* Selected sites instead of the single org/host choice.
|
|
45
|
+
* Selected sites instead of the single org/host choice. Each carries the
|
|
46
|
+
* site's own `disabledPlugins` when the shell has it (AGL-4047), which the
|
|
47
|
+
* installed-sites panel reads.
|
|
45
48
|
*/
|
|
46
|
-
hosts?: ReadonlyArray<
|
|
47
|
-
id: string;
|
|
48
|
-
label: string;
|
|
49
|
-
}>;
|
|
49
|
+
hosts?: ReadonlyArray<OrgInstallHost>;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Marketplace listing detail (AGL-95/419), relocated from the app route —
|
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
|
-
import { LISTING_README_MAX_CHARS, listingArtifactType, installsUnreviewedFallback, listingArtifactLabel, listingInclusions, hasLivePurchaseOf, hasOrgLicenceOf, licensedOrgIdsFor, installTargetsFor, isListingDeleted, isPrivateListing, resolveInstallPlan, resolveOrgInstallSummary, resolvePluginInstallState, resolveUninstallTargets, safePublisherHref, safePublisherImageSrc } from "../model/marketplace.js";
|
|
19
|
+
import { LISTING_README_MAX_CHARS, listingArtifactType, installsUnreviewedFallback, listingArtifactLabel, listingInclusions, hasLivePurchaseOf, hasOrgLicenceOf, licensedOrgIdsFor, installTargetsFor, isListingDeleted, isPrivateListing, resolveInstallPlan, resolveOrgInstallSummary, resolvePluginInstallState, resolveUninstallTargets, safePublisherHref, safePublisherImageSrc, allExceptInstallKind } from "../model/marketplace.js";
|
|
20
20
|
import { isPublisherMemberElsewhere, landsInPublisherWorkspace, publisherMemberBuyReason } from "../model/own-listing.js";
|
|
21
21
|
import { BlockedControl } from "@aglyn/shared-ui-jsx/components/blocked-control.component";
|
|
22
22
|
import { PrerequisiteNotice } from "@aglyn/shared-ui-jsx/components/prerequisite-notice.component";
|
|
23
|
+
import { SiteSharingField } from "@aglyn/shared-ui-jsx/components/site-sharing-field.component";
|
|
24
|
+
import { orgPermissionLabel } from "@aglyn/aglyn/app-utils/org-permissions";
|
|
23
25
|
import UninstallImpactDialog from "./uninstall-impact-dialog.component.js";
|
|
24
26
|
import { ListingImage } from "./listing-image.component.js";
|
|
25
27
|
import { buildRoute, parseMarkdownLite, PLATFORM_BRAND_NAME, offeredPluginVersion, PLUGIN_HOST_ABI_VERSION, pluginDocsHelp, resolveMarkdownLink, resolveMediaSrc, Route } from "@aglyn/aglyn";
|
|
@@ -27,7 +29,7 @@ import { mdiCheckCircle, mdiCheckCircleOutline, mdiEmailOutline, mdiGithub, mdiI
|
|
|
27
29
|
import { AppLink, CardDisplay, GridItems, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
28
30
|
import { NextPageTitle } from "@aglyn/shared-ui-next/contexts/next-page-title-provider";
|
|
29
31
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
30
|
-
import { Alert, Avatar, Box, Button,
|
|
32
|
+
import { Alert, Avatar, Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, Link as MuiLink, Rating, Stack, Tooltip, Typography } from "@mui/material";
|
|
31
33
|
import { collection, doc, documentId, getDoc, limit, orderBy, query, where } from "firebase/firestore";
|
|
32
34
|
import { useEffect, useMemo, useState } from "react";
|
|
33
35
|
import { useConsoleHostRoute, useFirestore, useFirestoreCollection, useFirestoreDoc, useHostOrgId, useUser, useScopeTokens } from "@aglyn/tenant-feature-instance";
|
|
@@ -427,15 +429,23 @@ export function ListingReadme({ readme }) {
|
|
|
427
429
|
const [updateOpen, setUpdateOpen] = useState(false);
|
|
428
430
|
// Screenshot lightbox (AGL-869): the clicked screenshot's URL, or null.
|
|
429
431
|
const [lightboxUrl, setLightboxUrl] = useState(null);
|
|
430
|
-
// Org-scope install targeting (AGL-773): All sites
|
|
431
|
-
// engaged at org scope with a known site
|
|
432
|
-
// simpler org/host choice (AGL-656) still
|
|
432
|
+
// Org-scope install targeting (AGL-773): All sites, a chosen subset, or all
|
|
433
|
+
// sites except some (AGL-4047). Only engaged at org scope with a known site
|
|
434
|
+
// list; otherwise the per-site tab's simpler org/host choice (AGL-656) still
|
|
435
|
+
// applies.
|
|
433
436
|
const orgTargeting = Boolean(orgScoped && hosts && hosts.length > 0);
|
|
434
|
-
const [
|
|
435
|
-
|
|
437
|
+
const [installTo, setInstallTo] = useState({
|
|
438
|
+
mode: 'all'
|
|
439
|
+
});
|
|
436
440
|
const allHostIds = useMemo(()=>(hosts != null ? hosts : []).map((h)=>h.id), [
|
|
437
441
|
hosts
|
|
438
442
|
]);
|
|
443
|
+
const installSites = useMemo(()=>(hosts != null ? hosts : []).map((host)=>({
|
|
444
|
+
id: host.id,
|
|
445
|
+
name: host.label
|
|
446
|
+
})), [
|
|
447
|
+
hosts
|
|
448
|
+
]);
|
|
439
449
|
// Listings are org-owned (AGL-652) — "did I publish this" is an org
|
|
440
450
|
// comparison, resolved from the routing mirror like the browse grid.
|
|
441
451
|
const [viewerOrgId, setViewerOrgId] = useState(null);
|
|
@@ -922,15 +932,49 @@ export function ListingReadme({ readme }) {
|
|
|
922
932
|
// artifact can host-pin (plugins, components, templates, layouts) — an
|
|
923
933
|
// org-only artifact can't target a subset.
|
|
924
934
|
const canSelectSites = installTargets.includes('host');
|
|
935
|
+
// "All sites except…" (AGL-4047): a plugin pins once for the workspace and
|
|
936
|
+
// leaves the chosen sites out, so a site added later gets it; a per-site copy
|
|
937
|
+
// only picks today's sites; an org-only artifact can't leave one out.
|
|
938
|
+
const allExceptKind = listing ? allExceptInstallKind(listing) : null;
|
|
939
|
+
const installModes = useMemo(()=>[
|
|
940
|
+
'all',
|
|
941
|
+
...canSelectSites ? [
|
|
942
|
+
'selected'
|
|
943
|
+
] : [],
|
|
944
|
+
...allExceptKind ? [
|
|
945
|
+
'allExcept'
|
|
946
|
+
] : []
|
|
947
|
+
], [
|
|
948
|
+
canSelectSites,
|
|
949
|
+
allExceptKind
|
|
950
|
+
]);
|
|
951
|
+
// A choice the artifact doesn't offer reads as All sites, never as a plan
|
|
952
|
+
// that silently means something else.
|
|
953
|
+
const installChoice = useMemo(()=>installModes.includes(installTo.mode) ? installTo : {
|
|
954
|
+
mode: 'all'
|
|
955
|
+
}, [
|
|
956
|
+
installModes,
|
|
957
|
+
installTo
|
|
958
|
+
]);
|
|
959
|
+
const targeting = installChoice.mode === 'selected' ? 'selected-sites' : installChoice.mode === 'allExcept' ? 'all-sites-except' : 'all-sites';
|
|
925
960
|
const installPlanSteps = useMemo(()=>listing ? resolveInstallPlan(listing, targeting, {
|
|
926
|
-
selectedHostIds,
|
|
927
|
-
allHostIds
|
|
961
|
+
selectedHostIds: installChoice.mode === 'selected' ? installChoice.hostIds : [],
|
|
962
|
+
allHostIds,
|
|
963
|
+
exceptHostIds: installChoice.mode === 'allExcept' ? installChoice.exceptHostIds : []
|
|
928
964
|
}) : [], [
|
|
929
965
|
listing,
|
|
930
966
|
targeting,
|
|
931
|
-
|
|
967
|
+
installChoice,
|
|
932
968
|
allHostIds
|
|
933
969
|
]);
|
|
970
|
+
const installModeDetails = installTargets.includes('org') ? {
|
|
971
|
+
all: 'Every site, including sites you add later.',
|
|
972
|
+
allExcept: 'Every site but the ones you leave out, including sites you add later.'
|
|
973
|
+
} : {
|
|
974
|
+
all: `Installs to all ${allHostIds.length} site` + (allHostIds.length === 1 ? '' : 's') + ". New sites won't get it automatically.",
|
|
975
|
+
allExcept: 'Installs to the sites you have today, but the ones you leave ' + "out. New sites won't get it automatically."
|
|
976
|
+
};
|
|
977
|
+
const installToProblem = installChoice.mode === 'selected' && !installChoice.hostIds.length ? 'Pick at least one site.' : installChoice.mode === 'allExcept' && !installChoice.exceptHostIds.length ? 'Choose at least one site to leave out, or choose All sites.' : null;
|
|
934
978
|
const versionHistory = Array.isArray(listing == null ? void 0 : listing.versionHistory) ? [
|
|
935
979
|
...listing.versionHistory
|
|
936
980
|
].sort((a, b)=>b.version - a.version) : [];
|
|
@@ -940,8 +984,18 @@ export function ListingReadme({ readme }) {
|
|
|
940
984
|
// something else.
|
|
941
985
|
const installTargetSummary = ()=>{
|
|
942
986
|
if (orgTargeting && !installed) {
|
|
943
|
-
var _installPlanSteps_;
|
|
944
|
-
|
|
987
|
+
var _installPlanSteps_, _orgStep_exceptHostIds;
|
|
988
|
+
const orgStep = installPlanSteps.length === 1 && ((_installPlanSteps_ = installPlanSteps[0]) == null ? void 0 : _installPlanSteps_.scope) === 'org' ? installPlanSteps[0] : null;
|
|
989
|
+
if (orgStep == null ? void 0 : (_orgStep_exceptHostIds = orgStep.exceptHostIds) == null ? void 0 : _orgStep_exceptHostIds.length) {
|
|
990
|
+
// "All sites except…" (AGL-4047): the sites left out, by name.
|
|
991
|
+
const leftOut = orgStep.exceptHostIds.map((hostId)=>{
|
|
992
|
+
var _ref;
|
|
993
|
+
var _find;
|
|
994
|
+
return (_ref = (_find = (hosts != null ? hosts : []).find((host)=>host.id === hostId)) == null ? void 0 : _find.label) != null ? _ref : hostId;
|
|
995
|
+
});
|
|
996
|
+
return `every site except ${leftOut.join(', ')}, ` + 'including sites added later';
|
|
997
|
+
}
|
|
998
|
+
if (orgStep) {
|
|
945
999
|
return 'the whole organization — every site, including sites added later';
|
|
946
1000
|
}
|
|
947
1001
|
const names = installPlanSteps.filter((step)=>step.scope === 'host').map((step)=>{
|
|
@@ -965,10 +1019,13 @@ export function ListingReadme({ readme }) {
|
|
|
965
1019
|
// and which are still free to add. Only meaningful at org scope for a
|
|
966
1020
|
// plugin — everything else installs INTO a site and has no pin to survey.
|
|
967
1021
|
const orgPluginScope = Boolean(orgTargeting && isPlugin);
|
|
968
|
-
const orgInstall = useMemo(
|
|
1022
|
+
const orgInstall = useMemo(// With each site's own switch (AGL-4047), so a site that turned the plugin
|
|
1023
|
+
// off, or was left out at install, is not listed as running it.
|
|
1024
|
+
()=>resolveOrgInstallSummary(hosts != null ? hosts : [], sitePins, orgPin != null ? orgPin : null, listingId), [
|
|
969
1025
|
hosts,
|
|
970
1026
|
sitePins,
|
|
971
|
-
orgPin
|
|
1027
|
+
orgPin,
|
|
1028
|
+
listingId
|
|
972
1029
|
]);
|
|
973
1030
|
/** Run a pin write, then re-read every site's pin. */ const runSiteEdit = async (work)=>{
|
|
974
1031
|
try {
|
|
@@ -1428,68 +1485,28 @@ export function ListingReadme({ readme }) {
|
|
|
1428
1485
|
})
|
|
1429
1486
|
}) : null,
|
|
1430
1487
|
orgTargeting && !installed && !(orgPluginScope && orgInstall.installedAnywhere) ? /*#__PURE__*/ _jsxs(Stack, {
|
|
1431
|
-
spacing: 1
|
|
1488
|
+
spacing: 1,
|
|
1432
1489
|
sx: {
|
|
1433
1490
|
maxWidth: 420
|
|
1434
1491
|
},
|
|
1435
1492
|
children: [
|
|
1436
|
-
/*#__PURE__*/
|
|
1437
|
-
select: true,
|
|
1438
|
-
size: "small",
|
|
1493
|
+
/*#__PURE__*/ _jsx(SiteSharingField, {
|
|
1439
1494
|
label: "Install to",
|
|
1440
|
-
value:
|
|
1441
|
-
onChange:
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
children: 'Selected sites'
|
|
1451
|
-
}) : null
|
|
1452
|
-
]
|
|
1495
|
+
value: installChoice,
|
|
1496
|
+
onChange: setInstallTo,
|
|
1497
|
+
sites: installSites,
|
|
1498
|
+
sitesReady: true,
|
|
1499
|
+
modes: installModes,
|
|
1500
|
+
modeDetails: installModeDetails,
|
|
1501
|
+
listLabels: {
|
|
1502
|
+
selected: 'Sites to install to'
|
|
1503
|
+
},
|
|
1504
|
+
problem: installToProblem
|
|
1453
1505
|
}),
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
children:
|
|
1458
|
-
/*#__PURE__*/ _jsx(FormLabel, {
|
|
1459
|
-
component: "legend",
|
|
1460
|
-
sx: {
|
|
1461
|
-
typography: 'caption'
|
|
1462
|
-
},
|
|
1463
|
-
children: 'Sites'
|
|
1464
|
-
}),
|
|
1465
|
-
/*#__PURE__*/ _jsx(FormGroup, {
|
|
1466
|
-
// Scrolls rather than pushing Install off
|
|
1467
|
-
// the page on an org with many sites.
|
|
1468
|
-
sx: {
|
|
1469
|
-
maxHeight: 240,
|
|
1470
|
-
overflowY: 'auto'
|
|
1471
|
-
},
|
|
1472
|
-
children: (hosts != null ? hosts : []).map((host)=>/*#__PURE__*/ _jsx(FormControlLabel, {
|
|
1473
|
-
control: /*#__PURE__*/ _jsx(Checkbox, {
|
|
1474
|
-
size: "small",
|
|
1475
|
-
checked: selectedHostIds.includes(host.id),
|
|
1476
|
-
onChange: (event)=>setSelectedHostIds((current)=>event.target.checked ? [
|
|
1477
|
-
...current,
|
|
1478
|
-
host.id
|
|
1479
|
-
] : current.filter((id)=>id !== host.id))
|
|
1480
|
-
}),
|
|
1481
|
-
label: host.label,
|
|
1482
|
-
slotProps: {
|
|
1483
|
-
typography: {
|
|
1484
|
-
variant: 'body2'
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}, host.id))
|
|
1488
|
-
}),
|
|
1489
|
-
/*#__PURE__*/ _jsx(FormHelperText, {
|
|
1490
|
-
children: selectedHostIds.length ? `${selectedHostIds.length} of ${(hosts != null ? hosts : []).length} selected.` : 'Pick at least one site.'
|
|
1491
|
-
})
|
|
1492
|
-
]
|
|
1506
|
+
installChoice.mode === 'allExcept' && allExceptKind === 'lasting' ? /*#__PURE__*/ _jsx(Typography, {
|
|
1507
|
+
variant: "caption",
|
|
1508
|
+
color: "text.secondary",
|
|
1509
|
+
children: 'Each site you leave out has the plugin ' + 'switched off, with the same switch as its ' + 'own Admin › Plugins page. Leaving a site ' + 'out takes ' + `“${orgPermissionLabel('site.manage')}” ` + 'there, and anyone holding it, which the ' + 'site’s admins do by default, can turn the ' + 'plugin back on for that site.'
|
|
1493
1510
|
}) : null
|
|
1494
1511
|
]
|
|
1495
1512
|
}) : installTargets.length > 1 && !installed ? /*#__PURE__*/ _jsxs(TextField, {
|