-
Notifications
You must be signed in to change notification settings - Fork 409
Description
Category
[ ] Enhancement
[X] Bug
[ ] Question
Version
Please specify what version of the library you are using: [ 3.12.0 ]
Expected / Desired Behavior / Question
The folder picker should allow setting the rootFolder to any location the current has permission to access.
Observed Behavior
If the rootFolder is set to a folder in another site collection, the picker is empty and the new folder action fails :

When the "Select folder" icon is click the dev console emits :
The failing network request is:
https://myTenant.sharepoint.com/sites/sitecoll1/_api/web/getFolderByServerRelativePath(decodedUrl='%2Fsites%2Fsitecoll2%2FDocuments%20partages')/folders?$select=Name,ServerRelativeUrl&$orderby=Name%20asc
And its result is:
{
"odata.error": {
"code": "-2147024809, System.ArgumentException",
"message": {
"lang": "en-US",
"value": "Server relative urls must start with SPWeb.ServerRelativeUrl"
}
}
}
My Analysis
Looking at the failing request, we can see the getFolderByServerRelativePath method is called onto the current page's web. Not the web targeted by the root folder, which is required.
Steps to Reproduce
Create a webpart which use FolderPicker and point to a folder outside the current site collection.
My repro code (I can push the full project if needed):
import { FolderPicker, IFolder } from '@pnp/spfx-controls-react';
import * as React from 'react';
import { BaseComponentContext } from '@microsoft/sp-component-base';
import { SPHttpClient } from '@microsoft/sp-http';
import { ISite, SitePicker } from '@pnp/spfx-controls-react/lib/SitePicker';
import { Stack } from 'office-ui-fabric-react';
export type FolderPickerTestProps = {
context: BaseComponentContext
}
type LibraryInfo = {
webUrl: string;
name: string;
}
// Ask the graph to provide the default library for a site
const getDefaultLibrary = async (context: BaseComponentContext, siteAbsoluteUrl: string): Promise<LibraryInfo> => {
const apiUrl = `${siteAbsoluteUrl}/_api/v2.0/drive?$select=webUrl,name`;
const response = await context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1);
if (!response.ok) {
throw new Error(`[${response.status}] Error fetching default library: ${await response.text()}`);
}
return await response.json();
}
const FolderPickerTest: React.FC<FolderPickerTestProps> = ({ context }) => {
const [defaultLibrary, setDefaultLibrary] = React.useState<LibraryInfo | undefined>(undefined);
const [selectedFolder, setSelectedFolder] = React.useState<IFolder | undefined>(undefined);
// After a site is selected, get its default library
const onSiteSelect = (site: ISite | undefined): void => {
if (!site?.url) { setDefaultLibrary(undefined); return; }
getDefaultLibrary(context, site.url)
.then(setDefaultLibrary)
.catch((error) => alert);
}
const data = { defaultLibrary, selectedFolder, };
return (
<Stack>
<SitePicker context={context} onChange={selectedSites => onSiteSelect(selectedSites[0])} multiSelect={false} />
{defaultLibrary && (
<FolderPicker context={context}
label={''}
rootFolder={{
Name: defaultLibrary.name,
ServerRelativeUrl: decodeURIComponent(new URL(defaultLibrary.webUrl).pathname), // Get the server relative url, with spaces decoded
}}
onSelect={folder => {
setSelectedFolder({ ...folder });
}}
canCreateFolders
/>
)}
<h3>Data:</h3>
<pre>
<code>{JSON.stringify(data, null, 2)}</code>
</pre>
</Stack>
);
}
export { FolderPickerTest };
