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",
|
"name": "organization-chart-webpart",
|
||||||
"title": "organization-chart-webpart",
|
"title": "organization-chart-webpart",
|
||||||
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
"id": "b2f88ea8-f7da-4d0e-a945-6973bf1acd1e",
|
||||||
"version": "1.0.1.0",
|
"version": "1.0.2.0",
|
||||||
"includeClientSideAssets": true,
|
"includeClientSideAssets": true,
|
||||||
"skipFeatureDeployment": true
|
"skipFeatureDeployment": true
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "organization-chart-webpart",
|
"name": "organization-chart-webpart",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"engines": { "node": ">=6.9.0 <9.0.0" },
|
"engines": { "node": ">=6.9.0 <9.0.0" },
|
||||||
|
|||||||
@@ -29,6 +29,17 @@ interface ITreeLoadState {
|
|||||||
visited: { [account: string]: boolean };
|
visited: { [account: string]: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IPeoplePickerEntityData {
|
||||||
|
AccountName?: string;
|
||||||
|
Email?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPeoplePickerEntity {
|
||||||
|
EntityData?: IPeoplePickerEntityData;
|
||||||
|
IsResolved?: boolean;
|
||||||
|
Key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class OrganizationChartDataService {
|
export class OrganizationChartDataService {
|
||||||
private static cache: { [account: string]: IProfileCacheEntry } = {};
|
private static cache: { [account: string]: IProfileCacheEntry } = {};
|
||||||
|
|
||||||
@@ -49,7 +60,7 @@ export class OrganizationChartDataService {
|
|||||||
visited: {}
|
visited: {}
|
||||||
};
|
};
|
||||||
const rootRequest: Promise<IUserProfile> = account
|
const rootRequest: Promise<IUserProfile> = account
|
||||||
? this.getProfile(account, cacheMinutes)
|
? this.getConfiguredProfile(account, cacheMinutes)
|
||||||
: this.getMyProfile(cacheMinutes);
|
: this.getMyProfile(cacheMinutes);
|
||||||
return rootRequest
|
return rootRequest
|
||||||
.then((profile: IUserProfile): Promise<IOrganizationNode> =>
|
.then((profile: IUserProfile): Promise<IOrganizationNode> =>
|
||||||
@@ -83,6 +94,79 @@ export class OrganizationChartDataService {
|
|||||||
return this.requestProfile(url, key, cacheMinutes);
|
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> {
|
private requestProfile(url: string, cacheKey: string, cacheMinutes: number): Promise<IUserProfile> {
|
||||||
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
||||||
.then((response: SPHttpClientResponse): Promise<any> => { // tslint:disable-line:no-any
|
.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/GetPropertiesFor') >= 0, 'User Profile REST access is missing.');
|
||||||
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetMyProperties') >= 0,
|
assert(service.indexOf('/_api/SP.UserProfiles.PeopleManager/GetMyProperties') >= 0,
|
||||||
'Reliable current-user profile access is missing.');
|
'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,
|
assert(service.indexOf('profile.directReports') >= 0 && service.indexOf('this.buildNode(childAccount, depth + 1') >= 0,
|
||||||
'Recursive downward organization loading is missing.');
|
'Recursive downward organization loading is missing.');
|
||||||
assert(webPart.indexOf("PropertyPaneTextField('startUser'") >= 0, 'Starting-person configuration is missing.');
|
assert(webPart.indexOf("PropertyPaneTextField('startUser'") >= 0, 'Starting-person configuration is missing.');
|
||||||
|
|||||||
Reference in New Issue
Block a user