feat: Update version to 1.0.2 and enhance profile retrieval with People Picker integration
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"name": "organization-chart-webpart",
|
||||
"title": "organization-chart-webpart",
|
||||
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
||||
"version": "1.0.1.0",
|
||||
"version": "1.0.2.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "organization-chart-webpart",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": { "node": ">=6.9.0 <9.0.0" },
|
||||
|
||||
@@ -29,6 +29,17 @@ interface ITreeLoadState {
|
||||
visited: { [account: string]: boolean };
|
||||
}
|
||||
|
||||
interface IPeoplePickerEntityData {
|
||||
AccountName?: string;
|
||||
Email?: string;
|
||||
}
|
||||
|
||||
interface IPeoplePickerEntity {
|
||||
EntityData?: IPeoplePickerEntityData;
|
||||
IsResolved?: boolean;
|
||||
Key?: string;
|
||||
}
|
||||
|
||||
export class OrganizationChartDataService {
|
||||
private static cache: { [account: string]: IProfileCacheEntry } = {};
|
||||
|
||||
@@ -49,7 +60,7 @@ export class OrganizationChartDataService {
|
||||
visited: {}
|
||||
};
|
||||
const rootRequest: Promise<IUserProfile> = account
|
||||
? this.getProfile(account, cacheMinutes)
|
||||
? this.getConfiguredProfile(account, cacheMinutes)
|
||||
: this.getMyProfile(cacheMinutes);
|
||||
return rootRequest
|
||||
.then((profile: IUserProfile): Promise<IOrganizationNode> =>
|
||||
@@ -83,6 +94,79 @@ export class OrganizationChartDataService {
|
||||
return this.requestProfile(url, key, cacheMinutes);
|
||||
}
|
||||
|
||||
private getConfiguredProfile(accountName: string, cacheMinutes: number): Promise<IUserProfile> {
|
||||
return this.resolveAccountNames(accountName)
|
||||
.catch((): string[] => [accountName])
|
||||
.then((accounts: string[]): Promise<IUserProfile> =>
|
||||
this.getFirstAvailableProfile(accounts, cacheMinutes, 0));
|
||||
}
|
||||
|
||||
private getFirstAvailableProfile(
|
||||
accounts: string[],
|
||||
cacheMinutes: number,
|
||||
index: number,
|
||||
previousError?: Error
|
||||
): Promise<IUserProfile> {
|
||||
if (index >= accounts.length) {
|
||||
return Promise.reject(previousError || new Error('The configured user could not be resolved.'));
|
||||
}
|
||||
return this.getProfile(accounts[index], cacheMinutes)
|
||||
.catch((error: Error): Promise<IUserProfile> =>
|
||||
this.getFirstAvailableProfile(accounts, cacheMinutes, index + 1, error));
|
||||
}
|
||||
|
||||
private resolveAccountNames(accountName: string): Promise<string[]> {
|
||||
const url: string = this.webUrl +
|
||||
'/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser';
|
||||
const body: string = JSON.stringify({
|
||||
queryParams: {
|
||||
__metadata: { type: 'SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters' },
|
||||
AllowEmailAddresses: true,
|
||||
AllowMultipleEntities: false,
|
||||
AllUrlZones: false,
|
||||
MaximumEntitySuggestions: 10,
|
||||
PrincipalSource: 15,
|
||||
PrincipalType: 1,
|
||||
QueryString: accountName
|
||||
}
|
||||
});
|
||||
const headers: any = { // tslint:disable-line:no-any
|
||||
'Accept': 'application/json;odata=verbose',
|
||||
'Content-Type': 'application/json;odata=verbose',
|
||||
'OData-Version': '3.0'
|
||||
};
|
||||
return this.context.spHttpClient.post(url, SPHttpClient.configurations.v1, { headers: headers, body: body })
|
||||
.then((response: SPHttpClientResponse): Promise<any> => { // tslint:disable-line:no-any
|
||||
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
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
private requestProfile(url: string, cacheKey: string, cacheMinutes: number): Promise<IUserProfile> {
|
||||
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
||||
.then((response: SPHttpClientResponse): Promise<any> => { // tslint:disable-line:no-any
|
||||
|
||||
@@ -27,6 +27,8 @@ assert(manifest.componentType === 'WebPart' && manifest.alias === 'OrganizationC
|
||||
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetPropertiesFor') >= 0, 'User Profile REST access is missing.');
|
||||
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetMyProperties') >= 0,
|
||||
'Reliable current-user profile access is missing.');
|
||||
assert(service.indexOf('ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser') >= 0,
|
||||
'Configured-account resolution through the SharePoint People Picker is missing.');
|
||||
assert(service.indexOf('profile.directReports') >= 0 && service.indexOf('this.buildNode(childAccount, depth + 1') >= 0,
|
||||
'Recursive downward organization loading is missing.');
|
||||
assert(webPart.indexOf("PropertyPaneTextField('startUser'") >= 0, 'Starting-person configuration is missing.');
|
||||
|
||||
Reference in New Issue
Block a user