Skip to content

Commit eed1210

Browse files
committed
feat(hub-common): support multiple channel owners, additional updates to support creating and editin
affects: @esri/hub-common ISSUES CLOSED: 12468
1 parent 8f6342f commit eed1210

19 files changed

+427
-178
lines changed

packages/common/src/channels/HubChannel.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export class HubChannel
210210
const { transformEntityToEditor } = await import(
211211
"./_internal/transformEntityToEditor"
212212
);
213-
return transformEntityToEditor(this.entity);
213+
return transformEntityToEditor(this.entity, this.context);
214214
}
215215

216216
/**
@@ -222,9 +222,11 @@ export class HubChannel
222222
const { transformEditorToEntity } = await import(
223223
"./_internal/transformEditorToEntity"
224224
);
225-
return {
225+
this.entity = {
226226
...this.entity,
227227
...transformEditorToEntity(editor),
228228
};
229+
await this.save();
230+
return this.entity;
229231
}
230232
}

packages/common/src/channels/_internal/ChannelUiSchemaCreate.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ export const buildUiSchema = async (
1717
context: IArcGISContext
1818
): Promise<IUiSchema> => {
1919
const facet = {
20-
labelKey: `${i18nScope}.fields.group.picker.facets.label`,
20+
label: `{{${i18nScope}.sections.group.picker.facets.label:translate}}`,
2121
key: "groups",
2222
display: "single-select",
2323
operation: "OR",
2424
options: [
2525
{
26-
labelKey: `${i18nScope}.fields.group.picker.facets.myGroups.label`,
26+
label: `{{${i18nScope}.sections.group.picker.facets.myGroups.label:translate}}`,
2727
key: "my-group",
2828
selected: true,
2929
predicates: [
@@ -34,7 +34,7 @@ export const buildUiSchema = async (
3434
],
3535
},
3636
{
37-
labelKey: `${i18nScope}.fields.group.picker.facets.myOrganization.label`,
37+
label: `{{${i18nScope}.sections.group.picker.facets.myOrganization.label:translate}}`,
3838
key: "my-organization",
3939
selected: false,
4040
predicates: [
@@ -50,7 +50,7 @@ export const buildUiSchema = async (
5050
};
5151
if (context.communityOrgId) {
5252
facet.options.push({
53-
labelKey: `${i18nScope}.fields.group.picker.facets.myCommunity.label`,
53+
label: `{{${i18nScope}.sections.group.picker.facets.myCommunity.label:translate}}`,
5454
key: "my-community",
5555
selected: false,
5656
predicates: [
@@ -126,7 +126,7 @@ export const buildUiSchema = async (
126126
type: "ERROR",
127127
keyword: "format",
128128
icon: true,
129-
label: `${i18nScope}.fields.blockWords.formatError`,
129+
labelKey: `${i18nScope}.fields.blockWords.formatError`,
130130
},
131131
],
132132
},
@@ -260,7 +260,7 @@ export const buildUiSchema = async (
260260
entityType: "group",
261261
catalogs: [
262262
getWellKnownCatalog(i18nScope, "allGroups", "group", {
263-
user: context?.currentUser,
263+
user: context.currentUser,
264264
}),
265265
],
266266
facets: [facet],

packages/common/src/channels/_internal/transformChannelToEntity.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function transformChannelToEntity(
1919
user: IUser
2020
): IHubChannel {
2121
return {
22+
access: channel.access,
2223
id: channel.id,
2324
name: channel.name,
2425
createdDate: new Date(channel.createdAt),
@@ -45,8 +46,29 @@ export function transformChannelToEntity(
4546
canDelete: canDeleteChannelV2(channel, user),
4647
orgId: channel.orgId,
4748
owner: channel.creator,
49+
channel,
4850

51+
// these don't apply to channels, yet they have to be defined or builds fail in hub-common:
52+
// - packages/common/src/associations/breakAssociation.ts
53+
// - packages/common/src/associations/requestAssociation.ts
54+
// - packages/common/src/content/_internal/ContentUiSchemaSettings.ts
55+
// - packages/common/src/templates/_internal/TemplateUiSchemaEdit.ts
56+
// the above are implemented in such a way that they assume every HubEntity has the below members...
57+
// those fns should be refactored to code to abstractions rather than concretions,
58+
// i.e. trait interfaces that define the properties that the source depends on
4959
typeKeywords: [],
50-
access: "private",
60+
thumbnail: undefined,
61+
62+
// these don't apply to channels, yet they have to be defined or builds fail in hub-components:
63+
// - packages/hub-components/src/utils/cardModelConverters/internal/utils.ts
64+
// - packages/hub-components/src/components/arcgis-hub-entity-view/about-components/arcgis-hub-entity-about/arcgis-hub-entity-about.tsx
65+
// - packages/hub-components/src/components/arcgis-hub-entity-view/hero-components/arcgis-hub-entity-hero/arcgis-hub-entity-hero.tsx
66+
// the above components are implemented in such a way that they assume every HubEntity has the below members...
67+
// those components should be rafactored to code to abstractions rather than concretions,
68+
// i.e. trait interfaces that define the properties that the source depends on
69+
view: undefined,
70+
description: undefined,
71+
location: undefined,
72+
tags: [],
5173
};
5274
}

packages/common/src/channels/_internal/transformEditorToEntity.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,8 @@ export function transformEditorToEntity(
1515
...editor.orgConfigs,
1616
...editor.groupConfigs,
1717
...editor.userConfigs,
18+
...editor.ownerConfigs,
1819
];
19-
if (editor.ownerConfig) {
20-
permissionValues.push(editor.ownerConfig);
21-
}
2220
return {
2321
name: editor.name,
2422
blockWords: editor.blockWords.split(",").map((val) => val.trim()),

packages/common/src/channels/_internal/transformEntityPermissionPoliciesToFormValues.ts

+45-22
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ const COLLABORATION_TYPE_TO_ENTITY_TYPE_MAP: Partial<
2323
[COLLABORATION_TYPES.user]: "user",
2424
[COLLABORATION_TYPES.group]: "group",
2525
[COLLABORATION_TYPES.groupAdmin]: "group",
26-
[COLLABORATION_TYPES.org]: "org",
27-
[COLLABORATION_TYPES.orgAdmin]: "org",
26+
[COLLABORATION_TYPES.org]: "organization",
27+
[COLLABORATION_TYPES.orgAdmin]: "organization",
2828
};
2929

3030
const COLLABORATION_TYPE_TO_ROLE_MAP: Partial<
@@ -246,28 +246,51 @@ export const transformEntityPermissionPoliciesToUserFormValues = (
246246
* Transforms an array of IEntityPermissionPolicy (Hub entity) objects to a IHubRoleConfigValue (entity editor) object
247247
* representing the channel's owner permissions
248248
* @param permissionPolicies An array of IEntityPermissionPolicy objects
249+
* @param defaultOrgId The current user's orgId
249250
* @returns an IHubRoleConfigValue object
250251
*/
251-
export const transformEntityPermissionPoliciesToOwnerFormValue = (
252-
permissionPolicies: IEntityPermissionPolicy[]
253-
): IHubRoleConfigValue => {
254-
const ownerPermission = permissionPolicies.find(
255-
({ permission }) => permission === CHANNEL_PERMISSIONS.channelOwner
256-
);
257-
return ownerPermission
258-
? {
259-
key: ownerPermission.collaborationId,
260-
entityId: ownerPermission.collaborationId,
261-
entityType:
262-
COLLABORATION_TYPE_TO_ENTITY_TYPE_MAP[
263-
ownerPermission.collaborationType
264-
],
265-
roles: {
266-
[COLLABORATION_TYPE_TO_ROLE_MAP[ownerPermission.collaborationType]]: {
267-
value: ownerPermission.permission,
268-
id: ownerPermission.id,
252+
export const transformEntityPermissionPoliciesToOwnerFormValues = (
253+
permissionPolicies: IEntityPermissionPolicy[],
254+
defaultOrgId: string
255+
): IHubRoleConfigValue[] => {
256+
const ownerConfigs: IHubRoleConfigValue[] = permissionPolicies.reduce<
257+
IHubRoleConfigValue[]
258+
>((acc, permissionPolicy) => {
259+
if (permissionPolicy.permission === CHANNEL_PERMISSIONS.channelOwner) {
260+
return [
261+
...acc,
262+
{
263+
key: permissionPolicy.collaborationId,
264+
entityId: permissionPolicy.collaborationId,
265+
entityType:
266+
COLLABORATION_TYPE_TO_ENTITY_TYPE_MAP[
267+
permissionPolicy.collaborationType
268+
],
269+
roles: {
270+
[COLLABORATION_TYPE_TO_ROLE_MAP[
271+
permissionPolicy.collaborationType
272+
]]: {
273+
value: permissionPolicy.permission,
274+
id: permissionPolicy.id,
275+
},
269276
},
270277
},
271-
}
272-
: null;
278+
];
279+
}
280+
return acc;
281+
}, []);
282+
if (!ownerConfigs.length) {
283+
ownerConfigs.push({
284+
key: defaultOrgId,
285+
entityId: defaultOrgId,
286+
entityType: COLLABORATION_TYPE_TO_ENTITY_TYPE_MAP["org-admin"],
287+
roles: {
288+
[COLLABORATION_TYPE_TO_ROLE_MAP["org-admin"]]: {
289+
value: CHANNEL_PERMISSIONS.channelOwner,
290+
id: undefined,
291+
},
292+
},
293+
});
294+
}
295+
return ownerConfigs;
273296
};
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { IHubChannel, IHubChannelEditor } from "../../core/types/IHubChannel";
2+
import { IArcGISContext } from "../../types/IArcGISContext";
23
import {
34
transformEntityPermissionPoliciesToGroupFormValues,
45
transformEntityPermissionPoliciesToOrgFormValues,
56
transformEntityPermissionPoliciesToPublicFormValues,
67
transformEntityPermissionPoliciesToUserFormValues,
7-
transformEntityPermissionPoliciesToOwnerFormValue,
8+
transformEntityPermissionPoliciesToOwnerFormValues,
89
} from "./transformEntityPermissionPoliciesToFormValues";
910

1011
/**
@@ -14,7 +15,8 @@ import {
1415
* @returns an IHubChannelEditor object
1516
*/
1617
export function transformEntityToEditor(
17-
entity: IHubChannel
18+
entity: IHubChannel,
19+
context: IArcGISContext
1820
): IHubChannelEditor {
1921
return {
2022
id: entity.id,
@@ -25,16 +27,18 @@ export function transformEntityToEditor(
2527
entity.permissions
2628
),
2729
orgConfigs: transformEntityPermissionPoliciesToOrgFormValues(
28-
entity.permissions
30+
entity.permissions,
31+
context.currentUser.orgId
2932
),
3033
groupConfigs: transformEntityPermissionPoliciesToGroupFormValues(
3134
entity.permissions
3235
),
3336
userConfigs: transformEntityPermissionPoliciesToUserFormValues(
3437
entity.permissions
3538
),
36-
ownerConfig: transformEntityPermissionPoliciesToOwnerFormValue(
37-
entity.permissions
39+
ownerConfigs: transformEntityPermissionPoliciesToOwnerFormValues(
40+
entity.permissions,
41+
context.currentUser.orgId
3842
),
3943
};
4044
}

packages/common/src/channels/_internal/transformFormValuesToEntityPermissionPolicies.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
1+
import { HubEntityType } from "../../core/types/HubEntityType";
12
import {
23
COLLABORATION_TYPES,
4+
CollaborationType,
35
IEntityPermissionPolicy,
46
} from "../../permissions/types";
5-
import { ChannelNonePermission } from "./ChannelBusinessRules";
7+
import {
8+
CHANNEL_PERMISSIONS,
9+
ChannelNonePermission,
10+
} from "./ChannelBusinessRules";
611
import { IHubRoleConfigValue } from "./transformEntityPermissionPoliciesToFormValues";
712

13+
const ENTITY_TYPE_TO_COLLABORATION_TYPE_MAP: Partial<
14+
Record<HubEntityType, { [key: string]: CollaborationType }>
15+
> = {
16+
user: {
17+
user: COLLABORATION_TYPES.user,
18+
},
19+
organization: {
20+
admin: COLLABORATION_TYPES.orgAdmin,
21+
member: COLLABORATION_TYPES.org,
22+
},
23+
group: {
24+
admin: COLLABORATION_TYPES.groupAdmin,
25+
member: COLLABORATION_TYPES.group,
26+
},
27+
};
28+
829
/**
930
* @private
1031
* Transforms an array of IHubRoleConfigValue objects into an array of IEntityPermissionPolicy objects
@@ -26,7 +47,15 @@ export function transformFormValuesToEntityPermissionPolicies(
2647
"collaborationType" | "collaborationId"
2748
>;
2849

29-
if (roleConfiguration.key === "public") {
50+
if (role.value === CHANNEL_PERMISSIONS.channelOwner) {
51+
permissionPolicy = {
52+
collaborationType:
53+
ENTITY_TYPE_TO_COLLABORATION_TYPE_MAP[
54+
roleConfiguration.entityType
55+
][roleKey],
56+
collaborationId: roleConfiguration.entityId,
57+
};
58+
} else if (roleConfiguration.key === "public") {
3059
permissionPolicy = {
3160
collaborationType:
3261
roleKey === "authenticated"

packages/common/src/core/fetchHubEntity.ts

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { fetchSurvey } from "../surveys/fetch";
1414
import { fetchEvent } from "../events/fetch";
1515
import { convertUserToHubUser, fetchHubUser } from "../users";
1616
import { fetchOrganization } from "../org/fetch";
17+
import { fetchHubChannel } from "../channels/fetch";
1718

1819
/**
1920
* Fetch a Hub entity by identifier (id or slug)
@@ -41,6 +42,9 @@ export async function fetchHubEntity(
4142
case "discussion":
4243
result = await fetchDiscussion(identifier, context.hubRequestOptions);
4344
break;
45+
case "channel":
46+
result = await fetchHubChannel(identifier, context);
47+
break;
4448
case "page":
4549
result = await fetchPage(identifier, context.hubRequestOptions);
4650
break;

packages/common/src/core/schemas/internal/getEditorSchemas.ts

+33
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { SiteEditorType } from "../../../sites/_internal/SiteSchema";
1414
import { ProjectEditorType } from "../../../projects/_internal/ProjectSchema";
1515
import { InitiativeEditorType } from "../../../initiatives/_internal/InitiativeSchema";
1616
import { DiscussionEditorType } from "../../../discussions/_internal/DiscussionSchema";
17+
import { ChannelEditorType } from "../../../channels/_internal/ChannelSchema";
1718
import { PageEditorType } from "../../../pages/_internal/PageSchema";
1819
import { ContentEditorType } from "../../../content/_internal/ContentSchema";
1920
import { TemplateEditorType } from "../../../templates/_internal/TemplateSchema";
@@ -128,6 +129,38 @@ export async function getEditorSchemas(
128129

129130
break;
130131
// ----------------------------------------------------
132+
case "channel":
133+
const { ChannelSchema } = await import(
134+
"../../../channels/_internal/ChannelSchema"
135+
);
136+
schema = cloneObject(ChannelSchema);
137+
138+
const channelModule: IEntityEditorModuleType = await {
139+
"hub:channel:edit": () =>
140+
import("../../../channels/_internal/ChannelUiSchemaEdit"),
141+
"hub:channel:create": () =>
142+
import("../../../channels/_internal/ChannelUiSchemaCreate"),
143+
}[type as ChannelEditorType]();
144+
uiSchema = await channelModule.buildUiSchema(
145+
i18nScope,
146+
options as EntityEditorOptions,
147+
context
148+
);
149+
150+
// if we have the buildDefaults fn, then construct the defaults
151+
// TODO: when first implementing buildDefaults for discussions, remove the ignore line
152+
153+
/* istanbul ignore next */
154+
if (channelModule.buildDefaults) {
155+
defaults = await channelModule.buildDefaults(
156+
i18nScope,
157+
options as EntityEditorOptions,
158+
context
159+
);
160+
}
161+
162+
break;
163+
// ----------------------------------------------------
131164
case "project":
132165
const { ProjectSchema } = await import(
133166
"../../../projects/_internal/ProjectSchema"

0 commit comments

Comments
 (0)