Skip to content

Commit

Permalink
🚀 Adds 'Set Form Customizer' command to the SharePoint Framework Tool…
Browse files Browse the repository at this point in the history
…kit, Closes #348 (#383)

## 🎯 Aim

Adds 'Set Form Customizer' command to the SharePoint Framework Toolkit,
to set the view, edit or new form with a form customizer

## 📷 Result

https://github.com/user-attachments/assets/1ac118ed-ad4b-44bb-946f-94c228aa0d81

## ✅ What was done

- [X] Add action to set the view, edit & new form with a form customizer

## 🔗 Related issue

Closes #348
  • Loading branch information
nicodecleyre authored Jan 1, 2025
1 parent e09e726 commit 8a6183e
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 1 deletion.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@
"category": "SharePoint Framework Toolkit",
"icon": "$(sync)"
},
{
"command": "spfx-toolkit.setFormCustomizer",
"title": "Set Form Customizer",
"category": "SharePoint Framework Toolkit"
},
{
"command": "spfx-toolkit.showMoreActions",
"title": "...",
Expand Down
5 changes: 4 additions & 1 deletion src/constants/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,8 @@ export const Commands = {
enableAppCatalogApp: `${EXTENSION_NAME}.enableAppCatalogApp`,
disableAppCatalogApp: `${EXTENSION_NAME}.disableAppCatalogApp`,
upgradeAppCatalogApp: `${EXTENSION_NAME}.upgradeAppCatalogApp`,
showMoreActions: `${EXTENSION_NAME}.showMoreActions`
showMoreActions: `${EXTENSION_NAME}.showMoreActions`,

// Set form customizer
setFormCustomizer: `${EXTENSION_NAME}.setFormCustomizer`
};
1 change: 1 addition & 0 deletions src/panels/CommandPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ export class CommandPanel {

actionCommands.push(new ActionTreeItem('Add new component', '', { name: 'add', custom: false }, undefined, Commands.addToProject));
actionCommands.push(new ActionTreeItem('Scaffold CI/CD Workflow', '', { name: 'rocket', custom: false }, undefined, Commands.pipeline));
actionCommands.push(new ActionTreeItem('Set Form Customizer', '', { name: 'checklist', custom: false }, undefined, Commands.setFormCustomizer));
actionCommands.push(new ActionTreeItem('View samples', '', { name: 'library', custom: false }, undefined, Commands.samplesGallery));

window.registerTreeDataProvider('pnp-view-actions', new ActionTreeDataProvider(actionCommands));
Expand Down
110 changes: 110 additions & 0 deletions src/services/actions/CliActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export class CliActions {
subscriptions.push(
commands.registerCommand(Commands.upgradeAppCatalogApp, CliActions.upgradeAppCatalogApp)
);
subscriptions.push(
commands.registerCommand(Commands.setFormCustomizer, CliActions.setFormCustomizer)
);
}

/**
Expand Down Expand Up @@ -920,4 +923,111 @@ export class CliActions {
Notifications.error(`${fileName}.tour file not found in path ${path.join(wsFolder.uri.fsPath, '.tours')}. Cannot start Code Tour.`);
}
}

/**
* Sets the form customizer for a content type on a list.
*/
public static async setFormCustomizer() {
const relativeUrl = await window.showInputBox({
prompt: 'Enter the relative URL of the site',
ignoreFocusOut: true,
placeHolder: 'e.g., sites/sales',
validateInput: (input) => {
if (!input) {
return 'site URL is required';
}

const trimmedInput = input.trim();

if (trimmedInput.startsWith('https://')) {
return 'Please provide a relative URL, not an absolute URL.';
}
if (trimmedInput.startsWith('/')) {
return 'Please provide a relative URL without a leading slash.';
}

return undefined;
}
});

if (relativeUrl === undefined) {
Notifications.warning('No site URL provided. Setting form customizer aborted.');
return;
}

const siteUrl = `${EnvironmentInformation.tenantUrl}/${relativeUrl.trim()}`;

const listTitle = await window.showInputBox({
prompt: 'Enter the list title',
ignoreFocusOut: true,
validateInput: (value) => value ? undefined : 'List title is required'
});

if (!listTitle) {
Notifications.warning('No list title provided. Setting form customizer aborted.');
return;
}

const contentType = await window.showInputBox({
prompt: 'Enter the Content Type name',
ignoreFocusOut: true,
validateInput: (value) => value ? undefined : 'Content Type name is required'
});

if (!contentType) {
Notifications.warning('No content type name provided. Setting form customizer aborted.');
return;
}

const editFormClientSideComponentId = await window.showInputBox({
prompt: 'Enter the Edit form customizer ID (leave empty to skip)',
ignoreFocusOut: true
});

const newFormClientSideComponentId = await window.showInputBox({
prompt: 'Enter the New form customizer ID (leave empty to skip)',
ignoreFocusOut: true
});

const displayFormClientSideComponentId = await window.showInputBox({
prompt: 'Enter the View form customizer ID (leave empty to skip)',
ignoreFocusOut: true
});

const commandOptions: any = {
webUrl: siteUrl,
listTitle: listTitle,
name: contentType
};

if (editFormClientSideComponentId) {
commandOptions.EditFormClientSideComponentId = editFormClientSideComponentId;
}

if (newFormClientSideComponentId ) {
commandOptions.NewFormClientSideComponentId = newFormClientSideComponentId ;
}

if (displayFormClientSideComponentId) {
commandOptions.DisplayFormClientSideComponentId = displayFormClientSideComponentId;
}

await window.withProgress({
location: ProgressLocation.Notification,
title: 'Setting form customizer...',
cancellable: true
}, async (progress: Progress<{ message?: string; increment?: number }>) => {
try {
const result = await CliExecuter.execute('spo contenttype set', 'json', commandOptions);
if (result.stderr) {
Notifications.error(result.stderr);
} else {
Notifications.info('Form customizer set successfully.');
}
} catch (e: any) {
const message = e?.error?.message;
Notifications.error(message);
}
});
}
}

0 comments on commit 8a6183e

Please sign in to comment.