Skip to content

Commit edc1fc6

Browse files
Merge pull request #1903 from martinlingstuyl/dynamicform-subfolders
Adds ability to create files/folders in subfolder using DynamicForm. Closes #1901
2 parents 79c38b1 + d16f4bb commit edc1fc6

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed

docs/documentation/docs/controls/DynamicForm.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The `DynamicForm` can be configured with the following properties:
6666
| validationErrorDialogProps | IValidationErrorDialogProps | no | Specifies validation error dialog properties |
6767
| customIcons | { [ columnInternalName: string ]: string } | no | Specifies custom icons for the form. The key of this dictionary is the column internal name, the value is the Fluent UI icon name. |
6868
| storeLastActiveTab | boolean | no | When uploading files: Specifies if last active tab will be stored after the Upload panel has been closed. Note: the value of selected tab is stored in the queryString hash. Default - `true` |
69+
| folderPath | string | no | Server relative or library relative folder to create the item in. This option is only available for document libraries and works only when the contentTypeId is specified and has a base type of type Document or Folder. Defaults to the root folder of the library. |
6970

7071
## Validation Error Dialog Properties `IValidationErrorDialogProps`
7172
| Property | Type | Required | Description |

src/controls/dynamicForm/DynamicForm.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import "@pnp/sp/lists";
3131
import "@pnp/sp/content-types";
3232
import "@pnp/sp/folders";
3333
import "@pnp/sp/items";
34+
import { IFolder } from "@pnp/sp/folders";
3435
import { IInstalledLanguageInfo } from "@pnp/sp/presets/all";
3536
import { cloneDeep, isEqual } from "lodash";
3637
import { ICustomFormatting, ICustomFormattingBodySection, ICustomFormattingNode } from "../../common/utilities/ICustomFormatting";
@@ -599,7 +600,8 @@ export class DynamicForm extends React.Component<
599600

600601
const library = await sp.web.lists.getById(listId);
601602
const folderFileName = this.getFolderName(objects);
602-
const newFolder = await library.rootFolder.addSubFolderUsingPath(folderFileName);
603+
const folder = !this.props.folderPath ? library.rootFolder : await this.getFolderByPath(this.props.folderPath, library.rootFolder);
604+
const newFolder = await folder.addSubFolderUsingPath(folderFileName);
603605
const fields = await newFolder.listItemAllFields();
604606

605607
if (fields[idField]) {
@@ -675,8 +677,9 @@ export class DynamicForm extends React.Component<
675677
"_"
676678
).trim() // Replace not allowed chars in folder name and trim empty spaces at the start or end.
677679
: ""; // Empty string will be replaced by SPO with Folder Item ID
678-
679-
const fileCreatedResult = await library.rootFolder.files.addChunked(encodeURI(itemTitle), await selectedFile.downloadFileContent());
680+
681+
const folder = !this.props.folderPath ? library.rootFolder : await this.getFolderByPath(this.props.folderPath, library.rootFolder);
682+
const fileCreatedResult = await folder.files.addChunked(encodeURI(itemTitle), await selectedFile.downloadFileContent());
680683
const fields = await fileCreatedResult.file.listItemAllFields();
681684

682685
if (fields[idField]) {
@@ -1496,4 +1499,26 @@ export class DynamicForm extends React.Component<
14961499

14971500
return folderNameValue.replace(/["|*|:|<|>|?|/|\\||]/g, "_").trim();
14981501
}
1502+
1503+
/**
1504+
* Returns a pnp/sp folder object based on the folderPath and the library the folder is in.
1505+
* The folderPath can be a server relative path, but should be in the same library.
1506+
* @param folderPath The path to the folder coming from the component properties
1507+
* @param rootFolder The rootFolder object of the library
1508+
* @returns
1509+
*/
1510+
private getFolderByPath = async (folderPath: string, rootFolder: IFolder): Promise<IFolder> => {
1511+
const libraryFolder = await rootFolder();
1512+
const normalizedFolderPath = decodeURIComponent(folderPath).toLowerCase().replace(/\/$/, "");
1513+
const serverRelativeLibraryPath = libraryFolder.ServerRelativeUrl.toLowerCase().replace(/\/$/, "");
1514+
1515+
// In case of a server relative path in the same library, return the folder
1516+
if (`${normalizedFolderPath}/`.startsWith(`${serverRelativeLibraryPath}/`)) {
1517+
return sp.web.getFolderByServerRelativePath(normalizedFolderPath);
1518+
}
1519+
1520+
// In other cases, expect a list-relative path and return the folder
1521+
const folder = sp.web.getFolderByServerRelativePath(`${serverRelativeLibraryPath}/${normalizedFolderPath}`);
1522+
return folder;
1523+
};
14991524
}

src/controls/dynamicForm/IDynamicFormProps.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,11 @@ export interface IDynamicFormProps {
139139
* @default true
140140
*/
141141
storeLastActiveTab?: boolean;
142+
143+
/**
144+
* Library relative folder to create the item in.
145+
* This option is only available for document libraries and works only when the contentTypeId is specified and has a base type of type Document or Folder.
146+
* Defaults to the root folder.
147+
*/
148+
folderPath?: string;
142149
}

0 commit comments

Comments
 (0)