Skip to content

Commit 7a840a5

Browse files
committed
Custom widgets - force prefix name to prevent collisions
1 parent ae2d6a5 commit 7a840a5

File tree

6 files changed

+27
-12
lines changed

6 files changed

+27
-12
lines changed

src/admin/custom-widgets/customWidgetDetailsModal.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ChoiceGroup, CommandBarButton, DefaultButton, IChoiceGroupOption, Icon,
1515
import { DeleteConfirmationOverlay } from '../utils/components/deleteConfirmationOverlay';
1616
import { CopyableTextField } from '../utils/components/copyableTextField';
1717
import { UNIQUE_REQUIRED, validateField } from '../utils/validator';
18+
import { customWidgetPrefixName } from "../../components/custom-widget/ko/utils";
1819

1920
interface CustomWidgetDetailsModalState {
2021
isEdit: boolean,
@@ -100,15 +101,15 @@ export class CustomWidgetDetailsModal extends React.Component<CustomWidgetDetail
100101
await this.blobStorage.uploadBlob(`/${dataPath}index.html`, fallbackUiUnit8);
101102
await this.blobStorage.uploadBlob(`/${dataPath}editor.html`, fallbackUiUnit8);
102103

103-
this.widgetService.registerWidget(name, {
104+
this.widgetService.registerWidget(customWidgetPrefixName(name), {
104105
modelDefinition: CustomWidgetModel,
105106
componentBinder: KnockoutComponentBinder,
106107
componentDefinition: CustomWidgetViewModel,
107108
modelBinder: CustomWidgetModelBinder,
108109
viewModelBinder: CustomWidgetViewModelBinder
109110
});
110111

111-
this.widgetService.registerWidgetEditor(name, {
112+
this.widgetService.registerWidgetEditor(customWidgetPrefixName(name), {
112113
displayName: this.state.customWidget.displayName,
113114
category: widgetCategory,
114115
iconClass: "widget-icon widget-icon-component",
@@ -200,7 +201,7 @@ export class CustomWidgetDetailsModal extends React.Component<CustomWidgetDetail
200201
}
201202
<Stack className={`collapsible-section${!this.state.showInstructions ? ' hidden' : ''}`}>
202203
<Text block styles={{ root: { paddingTop: 20 } }}>
203-
Follow the steps below to create, implement, and deploy a custom widget.
204+
Follow the steps below to create, implement, and deploy a custom widget.
204205
<Link href="https://aka.ms/apimdocs/portal/customwidgets" target="_blank">Learn more</Link>.
205206
</Text>
206207
<ol>

src/components/custom-widget-list/createWidget.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import template from "./createWidget.html";
1414
import fallbackUi from "!!raw-loader!./fallbackUi.html";
1515
import { KnockoutComponentBinder } from "@paperbits/core/ko/knockoutComponentBinder";
1616
import { CustomWidgetEditorViewModel, CustomWidgetViewModel, CustomWidgetViewModelBinder } from "../custom-widget/ko";
17+
import { customWidgetPrefixName } from "../custom-widget/ko/utils";
1718

1819
const techToName: Record<ScaffoldTech, string> = {
1920
typescript: "TypeScript",
@@ -102,15 +103,15 @@ export class CreateWidget {
102103
await this.blobStorage.uploadBlob(`/${dataPath}index.html`, fallbackUiUnit8);
103104
await this.blobStorage.uploadBlob(`/${dataPath}editor.html`, fallbackUiUnit8);
104105

105-
this.widgetService.registerWidget(name, {
106+
this.widgetService.registerWidget(customWidgetPrefixName(name), {
106107
modelDefinition: CustomWidgetModel,
107108
componentBinder: KnockoutComponentBinder,
108109
componentDefinition: CustomWidgetViewModel,
109110
modelBinder: CustomWidgetModelBinder,
110111
viewModelBinder: CustomWidgetViewModelBinder
111112
});
112113

113-
this.widgetService.registerWidgetEditor(name, {
114+
this.widgetService.registerWidgetEditor(customWidgetPrefixName(name), {
114115
displayName: displayName,
115116
category: widgetCategory,
116117
iconClass: "widget-icon widget-icon-component",
@@ -128,7 +129,7 @@ export class CreateWidget {
128129
public async deleteWidget(): Promise<void> {
129130
if (!this.config) return alert("Didn't found config to delete.")
130131

131-
if (!confirm(`This operation is in-reversible, are you sure you want to delete custom widget '${this.config.displayName}'?`)) return;
132+
if (!confirm(`This operation is in-reversible, are you sure you want to delete custom widget '${this.config.displayName}' (ID: ${this.config.name})?`)) return;
132133

133134
const blobsToDelete = await this.blobStorage.listBlobs(buildBlobDataPath(this.config.name));
134135
blobsToDelete.push(buildBlobConfigPath(this.config.name));

src/components/custom-widget-list/customWidgetList.module.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { DevelopmentInstructions } from "./developmentInstructions";
1111
import { CopyCode } from "./copyCode";
1212
import { KnockoutComponentBinder } from "@paperbits/core/ko";
1313
import { CustomWidgetEditorViewModel, CustomWidgetViewModel, CustomWidgetViewModelBinder } from "../custom-widget/ko";
14+
import { customWidgetPrefixName } from "../custom-widget/ko/utils";
1415

1516
export class CustomWidgetListModule implements IInjectorModule {
1617
public register(injector: IInjector): void {
@@ -27,15 +28,15 @@ export class CustomWidgetListModule implements IInjectorModule {
2728

2829
const widgetService = injector.resolve<IWidgetService>("widgetService");
2930
configsPromise.then(configs => configs.forEach(config => {
30-
widgetService.registerWidget(config.name, {
31+
widgetService.registerWidget(customWidgetPrefixName(config.name), {
3132
modelDefinition: CustomWidgetModel,
3233
componentBinder: KnockoutComponentBinder,
3334
componentDefinition: CustomWidgetViewModel,
3435
modelBinder: CustomWidgetModelBinder,
3536
viewModelBinder: CustomWidgetViewModelBinder
3637
});
3738

38-
widgetService.registerWidgetEditor(config.name, {
39+
widgetService.registerWidgetEditor(customWidgetPrefixName(config.name), {
3940
displayName: config.displayName || config.name,
4041
category: widgetCategory,
4142
iconClass: "widget-icon widget-icon-component",

src/components/custom-widget/customWidgetHandlers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { IWidgetHandler } from "@paperbits/common/editing";
33
import { StyleHelper } from "@paperbits/styles";
44
import { CustomWidgetModel } from "./customWidgetModel";
55
import { sizeStylesInitial } from "./ko/constants";
6+
import { customWidgetPrefixName } from "./ko/utils";
67

78
export interface TCustomWidgetConfig extends CustomWidgetCommonConfig {
89
name: string;
@@ -17,8 +18,10 @@ export class CustomWidgetHandlers implements IWidgetHandler {
1718
}
1819

1920
public async getWidgetModel(): Promise<CustomWidgetModel> {
21+
const id = customWidgetPrefixName(this.configuration.name); // add prefix to prevent conflicts with existing widgets
22+
2023
const model = new CustomWidgetModel();
21-
model.name = this.configuration.name;
24+
model.name = id;
2225
model.displayName = this.configuration.displayName;
2326
model.customInputValue = "{}";
2427
StyleHelper.setPluginConfigForLocalStyles(model.styles, "size", sizeStylesInitial);

src/components/custom-widget/ko/customWidgetEditorView.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
<div class="flex-item">
44
<div class="form-group">
55
<label class="form-label">Name</label>
6-
<input type="text" class="form-control" data-bind="textInput: model.widgetDisplayName" disabled />
6+
<input type="text" class="form-control" data-bind="textInput: model.displayName" disabled />
7+
<input type="text" class="form-control" data-bind="textInput: model.name" disabled style="display: none" />
78
</div>
89

910
<size-editor params="{ features: 'width,height', allowUnits: 'px,%', sizeConfig: sizeStyleConfig, onUpdate: onSizeUpdate }">

src/components/custom-widget/ko/utils.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,27 @@ import * as Constants from "../../../constants";
99
import { MapiBlobStorage } from "../../../persistence";
1010
import { CustomWidgetModel } from "../customWidgetModel";
1111

12+
const customWidgetNamePrefix = "CustomWidget-"
13+
14+
export const customWidgetPrefixName = (name: string): string => {
15+
return customWidgetNamePrefix + name;
16+
}
17+
1218
export async function buildWidgetSource(
1319
blobStorage: MapiBlobStorage,
1420
model: CustomWidgetModel,
1521
environment: Environment,
1622
filePath: string,
1723
): Promise<{ override: string | null, src: string }> {
24+
const name = model.name.startsWith(customWidgetNamePrefix) ? model.name.replace(customWidgetNamePrefix, "") : model.name;
25+
1826
// check is necessary during publishing as window.sessionStorage.getItem throws "DOMException {} node:internal/process/promises:279"
1927
const developmentSrc = environment !== "publishing"
20-
? window.sessionStorage.getItem(Constants.overrideConfigSessionKeyPrefix + model.name)
28+
? window.sessionStorage.getItem(Constants.overrideConfigSessionKeyPrefix + name)
2129
: null;
2230

2331
const url = new URL(developmentSrc == null ? (
24-
await blobStorage.getDownloadUrlWithoutToken(`${buildBlobDataPath(model.name)}${filePath}`)
32+
await blobStorage.getDownloadUrlWithoutToken(`${buildBlobDataPath(name)}${filePath}`)
2533
) : developmentSrc + filePath);
2634

2735
url.pathname = decodeURIComponent(url.pathname);

0 commit comments

Comments
 (0)