Skip to content

Folder picker does not works if outside of current site collection #1379

@stevebeauge

Description

@stevebeauge

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 :
image

When the "Select folder" icon is click the dev console emits :

image

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 };

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions