@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
253 lines (230 loc) • 10.9 kB
text/typescript
import { firstIndexOf } from "../../helpers/collections.base";
import { contentTypes, jsonTypes } from "../../types/rest.types";
import { PrincipalType } from "../../types/sharepoint.types";
import { IUserInfo } from "../../types/sharepoint.utils.types";
import { GetJson } from "../rest";
import { EnsureUser } from "./user";
export interface iPeoplePickerUserItem {
/** LoginName or Id of the principal in the site. */
id: string;
/** LoginName of the principal. */
loginName: string;
imageUrl: string;
imageInitials: string;
text: string; // name
secondaryText: string; // role
tertiaryText: string; // status
optionalText: string; // anything
}
/**
* Service implementation to search people in SharePoint
*/
export class SPPeopleSearchService {
private cachedLocalUsers: { [siteUrl: string]: IUserInfo[] };
/**
* Service constructor
*/
constructor(private context: { siteUrl }) {
this.cachedLocalUsers = {};
this.cachedLocalUsers[this.context.siteUrl] = [];
}
/**
* Generate the user photo link using SharePoint user photo endpoint.
*
* @param value
*/
public generateUserPhotoLink(value: string, size: "S" | "M" = "M"): string {
return `${this.context.siteUrl}/_layouts/15/userphoto.aspx?accountname=${encodeURIComponent(value)}&size=M`;
}
/**
* Generate sum of principal types
*
* PrincipalType controls the type of entities that are returned in the results.
* Choices are All - 15, Distribution List - 2 , Security Groups - 4, SharePoint Groups - 8, User - 1.
* These values can be combined (example: 13 is security + SP groups + users)
*
* @param principalTypes
*/
public getSumOfPrincipalTypes(principalTypes: PrincipalType[]) {
return !!principalTypes && principalTypes.length > 0 ? principalTypes.reduce((a, b) => a + b, 0) : 1;
}
/**
* Retrieve the specified group
*
* @param groupName
* @param siteUrl
*/
public async getGroupId(groupName: string, siteUrl: string = null): Promise<number | null> {
// if (Environment.type === EnvironmentType.Local) {
// return 1;
// } else {
const groups = await this.searchTenant(siteUrl, groupName, 1, [PrincipalType.SharePointGroup], false, 0);
return (groups && groups.length > 0) ? parseInt(groups[0].id) : null;
//}
}
/**
* Search person by its email or login name
*/
public async searchPersonByEmailOrLogin(email: string, principalTypes: PrincipalType[], siteUrl: string = null, groupId: number = null, ensureUser: boolean = false): Promise<iPeoplePickerUserItem> {
// if (Environment.type === EnvironmentType.Local) {
// // If the running environment is local, load the data from the mock
// const mockUsers = await this.searchPeopleFromMock(email);
// return (mockUsers && mockUsers.length > 0) ? mockUsers[0] : null;
// } else {
const userResults = await this.searchTenant(siteUrl, email, 1, principalTypes, ensureUser, groupId);
return (userResults && userResults.length > 0) ? userResults[0] : null;
//}
}
/**
* Search All Users from the SharePoint People database
*/
public async searchPeople(query: string, maximumSuggestions: number, principalTypes: PrincipalType[], siteUrl: string = null, groupId: number = null, ensureUser: boolean = false): Promise<iPeoplePickerUserItem[]> {
// if (Environment.type === EnvironmentType.Local) {
// // If the running environment is local, load the data from the mock
// return this.searchPeopleFromMock(query);
// } else {
return await this.searchTenant(siteUrl, query, maximumSuggestions, principalTypes, ensureUser, groupId);
//}
}
/**
* Tenant search
*/
private async searchTenant(siteUrl: string, query: string, maximumSuggestions: number, principalTypes: PrincipalType[], ensureUser: boolean, groupId: number): Promise<iPeoplePickerUserItem[]> {
try {
// If the running env is SharePoint, loads from the peoplepicker web service
const userRequestUrl: string = `${siteUrl || this.context.siteUrl}/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser`;
const searchBody = {
queryParams: {
AllowEmailAddresses: true,
AllowMultipleEntities: false,
AllUrlZones: false,
MaximumEntitySuggestions: maximumSuggestions,
PrincipalSource: 15,
PrincipalType: this.getSumOfPrincipalTypes(principalTypes),
QueryString: query
}
};
// Search on the local site when "0"
if (siteUrl) {
searchBody.queryParams["SharePointGroupID"] = 0;
}
// Check if users need to be searched in a specific group
if (groupId) {
searchBody.queryParams["SharePointGroupID"] = groupId;
}
// Do the call against the People REST API endpoint
const userDataResp = await GetJson<{ value: string; }>(
userRequestUrl,
JSON.stringify(searchBody),
{
headers: {
Accept: jsonTypes.standard,
"content-type": contentTypes.json
}
})
if (userDataResp && userDataResp.value && userDataResp.value.length > 0) {
let values: any = userDataResp.value;
if (typeof userDataResp.value === "string") {
values = JSON.parse(userDataResp.value);
}
// Filter out "UNVALIDATED_EMAIL_ADDRESS"
values = values.filter(v => !(v.EntityData && v.EntityData.PrincipalType && v.EntityData.PrincipalType === "UNVALIDATED_EMAIL_ADDRESS"));
// Check if local user IDs need to be retrieved
if (ensureUser) {
for (const value of values) {
// Only ensure the user if it is not a SharePoint group
if (!value.EntityData || (value.EntityData && typeof value.EntityData.SPGroupID === "undefined")) {
const id = await this.ensureUser(value.Key);
value.LoginName = value.Key;
value.Key = id;
}
}
}
// Filter out NULL keys
values = values.filter(v => v.Key !== null);
const userResults = values.map(element => {
switch (element.EntityType) {
case 'User':
return {
id: element.Key,
loginName: element.LoginName ? element.LoginName : element.Key,
imageUrl: this.generateUserPhotoLink(element.Description || ""),
imageInitials: this.getFullNameInitials(element.DisplayText),
text: element.DisplayText, // name
secondaryText: element.EntityData.Email || element.Description, // email
tertiaryText: "", // status
optionalText: "" // anything
} as iPeoplePickerUserItem;
case 'SecGroup':
return {
id: element.Key,
loginName: element.LoginName ? element.LoginName : element.Key,
imageInitials: this.getFullNameInitials(element.DisplayText),
text: element.DisplayText,
secondaryText: element.ProviderName
} as iPeoplePickerUserItem;
case 'FormsRole':
return {
id: element.Key,
loginName: element.LoginName ? element.LoginName : element.Key,
imageInitials: this.getFullNameInitials(element.DisplayText),
text: element.DisplayText,
secondaryText: element.ProviderName
} as iPeoplePickerUserItem;
default:
return {
id: element.EntityData.SPGroupID,
loginName: element.EntityData.AccountName,
imageInitials: this.getFullNameInitials(element.DisplayText),
text: element.DisplayText,
secondaryText: element.EntityData.AccountName
} as iPeoplePickerUserItem;
}
});
return userResults;
}
// Nothing to return
return [];
} catch (e) {
console.error("PeopleSearchService::searchTenant: error occured while fetching the users.");
return [];
}
}
/**
* Retrieves the local user ID
*
* @param userId
*/
private async ensureUser(userId: string): Promise<number> {
const siteUrl = this.context.siteUrl;
if (this.cachedLocalUsers && this.cachedLocalUsers[siteUrl]) {
const users = this.cachedLocalUsers[siteUrl];
const userIdx = firstIndexOf(users, u => u.LoginName === userId);
if (userIdx !== -1) {
return users[userIdx].Id;
}
}
const user = await EnsureUser(siteUrl, userId)
if (user && user.Id) {
this.cachedLocalUsers[siteUrl].push(user);
return user.Id;
}
return null;
}
/**
* Generates Initials from a full name
*/
private getFullNameInitials(fullName: string): string {
if (fullName === null) {
return fullName;
}
const words: string[] = fullName.split(' ');
if (words.length === 0) {
return '';
} else if (words.length === 1) {
return words[0].charAt(0);
} else {
return (words[0].charAt(0) + words[1].charAt(0));
}
}
}