feat: Implement SharePoint People Picker for selecting starting person and update version to 1.0.3

This commit is contained in:
Torsten Brendgen
2026-08-24 14:28:01 +02:00
parent 4e5dafabfc
commit a686f8c055
13 changed files with 299 additions and 48 deletions
@@ -11,6 +11,7 @@ import {
} from './OrganizationChartCore';
import {
IOrganizationChartData,
IOrganizationChartPersonSuggestion,
IOrganizationNode,
IUserProfile,
IUserProfileProperty
@@ -35,6 +36,7 @@ interface IPeoplePickerEntityData {
}
interface IPeoplePickerEntity {
DisplayText?: string;
EntityData?: IPeoplePickerEntityData;
IsResolved?: boolean;
Key?: string;
@@ -49,7 +51,8 @@ export class OrganizationChartDataService {
accountName: string,
maxDepth: number,
maxPeople: number,
cacheMinutes: number
cacheMinutes: number,
alternateAccountName?: string
): Promise<IOrganizationChartData> {
const account: string = normalizeAccount(accountName);
const state: ITreeLoadState = {
@@ -60,7 +63,7 @@ export class OrganizationChartDataService {
visited: {}
};
const rootRequest: Promise<IUserProfile> = account
? this.getConfiguredProfile(account, cacheMinutes)
? this.getConfiguredProfile(account, cacheMinutes, alternateAccountName)
: this.getMyProfile(cacheMinutes);
return rootRequest
.then((profile: IUserProfile): Promise<IOrganizationNode> =>
@@ -94,11 +97,39 @@ export class OrganizationChartDataService {
return this.requestProfile(url, key, cacheMinutes);
}
private getConfiguredProfile(accountName: string, cacheMinutes: number): Promise<IUserProfile> {
public searchPeople(query: string): Promise<IOrganizationChartPersonSuggestion[]> {
const searchText: string = normalizeAccount(query);
if (searchText.length < 2) { return Promise.resolve([]); }
return this.requestPeoplePicker(searchText).then(
(entities: IPeoplePickerEntity[]): IOrganizationChartPersonSuggestion[] =>
entities.filter((entity: IPeoplePickerEntity): boolean =>
entity.IsResolved !== false && !!normalizeAccount(entity.Key))
.map((entity: IPeoplePickerEntity): IOrganizationChartPersonSuggestion => ({
accountName: normalizeAccount(entity.EntityData && entity.EntityData.AccountName),
displayName: normalizeAccount(entity.DisplayText) || normalizeAccount(entity.Key),
email: normalizeAccount(entity.EntityData && entity.EntityData.Email),
loginName: normalizeAccount(entity.Key)
}))
);
}
public validatePerson(loginName: string, accountName: string, cacheMinutes: number): Promise<boolean> {
const candidates: string[] = uniqueAccounts([loginName, accountName], '', 2);
return this.getFirstAvailableProfile(candidates, cacheMinutes, 0)
.then((): boolean => true, (): boolean => false);
}
private getConfiguredProfile(
accountName: string,
cacheMinutes: number,
alternateAccountName?: string
): Promise<IUserProfile> {
return this.resolveAccountNames(accountName)
.catch((): string[] => [accountName])
.then((accounts: string[]): Promise<IUserProfile> =>
this.getFirstAvailableProfile(accounts, cacheMinutes, 0));
.then((accounts: string[]): Promise<IUserProfile> => {
const candidates: string[] = uniqueAccounts(accounts.concat([alternateAccountName]), '', accounts.length + 1);
return this.getFirstAvailableProfile(candidates, cacheMinutes, 0);
});
}
private getFirstAvailableProfile(
@@ -116,6 +147,29 @@ export class OrganizationChartDataService {
}
private resolveAccountNames(accountName: string): Promise<string[]> {
return this.searchPeople(accountName).then(
(suggestions: IOrganizationChartPersonSuggestion[]): string[] => {
const input: string = normalizeAccount(accountName).toLowerCase();
const matching: IOrganizationChartPersonSuggestion[] = suggestions.filter(
(suggestion: IOrganizationChartPersonSuggestion): boolean =>
suggestion.accountName.toLowerCase() === input ||
suggestion.email.toLowerCase() === input ||
suggestion.loginName.toLowerCase() === input
);
const selected: IOrganizationChartPersonSuggestion[] = matching.length
? matching
: (suggestions.length === 1 ? suggestions : []);
const candidates: string[] = [];
selected.forEach((suggestion: IOrganizationChartPersonSuggestion): void => {
candidates.push(suggestion.loginName);
candidates.push(suggestion.accountName);
});
candidates.push(normalizeAccount(accountName));
return uniqueAccounts(candidates, '', candidates.length || 1);
});
}
private requestPeoplePicker(accountName: string): Promise<IPeoplePickerEntity[]> {
const url: string = this.webUrl +
'/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser';
const body: string = JSON.stringify({
@@ -140,30 +194,14 @@ export class OrganizationChartDataService {
if (response.ok) { return response.json(); }
return response.text().then((text: string): Promise<any> => // tslint:disable-line:no-any
Promise.reject(new Error('People Picker request failed (' + response.status + '): ' + text)));
}).then((payload: any): string[] => { // tslint:disable-line:no-any
}).then((payload: any): IPeoplePickerEntity[] => { // tslint:disable-line:no-any
const data: any = payload && payload.d !== undefined ? payload.d : payload; // tslint:disable-line:no-any
const serialized: string = String(
data && data.ClientPeoplePickerSearchUser !== undefined
? data.ClientPeoplePickerSearchUser
: (data && data.value !== undefined ? data.value : '[]')
);
let entities: IPeoplePickerEntity[] = [];
try { entities = JSON.parse(serialized) as IPeoplePickerEntity[]; } catch (error) { entities = []; }
const input: string = normalizeAccount(accountName).toLowerCase();
const matching: IPeoplePickerEntity[] = entities.filter((entity: IPeoplePickerEntity): boolean => {
const account: string = normalizeAccount(entity.EntityData && entity.EntityData.AccountName).toLowerCase();
const email: string = normalizeAccount(entity.EntityData && entity.EntityData.Email).toLowerCase();
const key: string = normalizeAccount(entity.Key).toLowerCase();
return entity.IsResolved !== false && (account === input || email === input || key === input);
});
const selected: IPeoplePickerEntity[] = matching.length ? matching : (entities.length === 1 ? entities : []);
const candidates: string[] = [];
selected.forEach((entity: IPeoplePickerEntity): void => {
candidates.push(normalizeAccount(entity.Key));
candidates.push(normalizeAccount(entity.EntityData && entity.EntityData.AccountName));
});
candidates.push(normalizeAccount(accountName));
return uniqueAccounts(candidates, '', candidates.length || 1);
try { return JSON.parse(serialized) as IPeoplePickerEntity[]; } catch (error) { return []; }
});
}