+ {this.props.GeneralFilter &&
+
+
this.clearGeneralFilter()} onChange={(ev, newValue) => this.filterListItemsByGeneralFilter(newValue, false, true, this.state.items, this.state.filterItems)} />
-
}
-
- {this.props.ShowClearAllFilters && clearAllButton}
+
+ }
+
+
+ {this.props.ShowClearAllFilters && clearAllButton}
+
-
-
- {this.props.ShowItemCount &&
{this.props.ItemCountText.replace("{itemCount}", `${this.state.filterItems.length}`)}
}
-
this._onRenderDetails(detailsFooterProps) : undefined}
- onRenderDetailsHeader={this._checkIndividualFilter("header") ? (detailsHeaderProps) => this._onRenderDetails(detailsHeaderProps) : undefined}
- selectionMode={SelectionMode.none}
- onItemInvoked={this.props.clickEnabled && !this.props.oneClickOption ? this._onItemInvoked : null}
- onRenderRow={(props, defaultRender) => this.getOnRowClickRender(props, defaultRender)}
- onRenderItemColumn={this._renderItemColumn}
- />
- {this.props.ShowPagination &&
-
-
-
-
-
}
- lastPageText={
}
- prevPageText={
}
- nextPageText={
}
- activeLinkClass={styles.active}
- itemsCountPerPage={this.props.ItemsInPage}
- totalItemsCount={this.state.filterItems ? this.state.filterItems.length : 0}
- pageRangeDisplayed={5}
- onChange={this.handlePageChange.bind(this)}
- />
-
+
+
+
+ {this.props.ShowItemCount &&
+
{this.props.ItemCountText.replace("{itemCount}", `${this.state.filterItems ? this.state.filterItems.length : 0}`)}
+ }
+
this._onRenderDetails(detailsFooterProps, this._checkIndividualFilter("footer"), false)}
+ onRenderDetailsHeader={(detailsHeaderProps) => this._onRenderDetails(detailsHeaderProps, this._checkIndividualFilter("header"), true)}
+ selectionMode={SelectionMode.none}
+ onItemInvoked={this.props.clickEnabled && !this.props.oneClickOption ? this._onItemInvoked : null}
+ onRenderRow={(props, defaultRender) => this.getOnRowClickRender(props, defaultRender)}
+ onRenderItemColumn={this._renderItemColumn}
+ shimmerLines={this.props.ShowPagination ? this.props.ItemsInPage : 10}
+ />
+ {this.props.ShowPagination &&
+
+
+
+
+
}
+ lastPageText={
}
+ prevPageText={
}
+ nextPageText={
}
+ activeLinkClass={styles.active}
+ itemsCountPerPage={this.props.ItemsInPage}
+ totalItemsCount={this.state.filterItems ? this.state.filterItems.length : 0}
+ pageRangeDisplayed={5}
+ onChange={this.handlePageChange.bind(this)}
+ />
- }
-
+
+ }
- }
+
+ }
);
}
-
}
diff --git a/samples/react-list-search/src/webparts/listSearch/loc/en-us.js b/samples/react-list-search/src/webparts/listSearch/loc/en-us.js
index ec23c9e11..8e1b98039 100644
--- a/samples/react-list-search/src/webparts/listSearch/loc/en-us.js
+++ b/samples/react-list-search/src/webparts/listSearch/loc/en-us.js
@@ -105,7 +105,9 @@ define([], function () {
InitialSearchText: "Initial search query",
GeneralFilterInitialQueryEnabled: "Initial search enabled",
GeneralFilterInitialQueryOption: "Initial search type",
- GeneralFilterInitialQueryTextValue: "Initial search value"
-
+ GeneralFilterInitialQueryTextValue: "Initial search value",
+ WebPartTitlePlaceHolder: "Web part title",
+ ModerAudienceEnabledTitle: "Modern audience enabled",
+ ModernAudienceEnabledTitle: "Modern audience enabled",
}
});
diff --git a/samples/react-list-search/src/webparts/listSearch/loc/mystrings.d.ts b/samples/react-list-search/src/webparts/listSearch/loc/mystrings.d.ts
index 00f5bc642..012257e44 100644
--- a/samples/react-list-search/src/webparts/listSearch/loc/mystrings.d.ts
+++ b/samples/react-list-search/src/webparts/listSearch/loc/mystrings.d.ts
@@ -105,6 +105,9 @@ declare interface IListSearchWebPartStrings {
GeneralFilterInitialQueryEnabled: string;
GeneralFilterInitialQueryOption: string;
GeneralFilterInitialQueryTextValue: string;
+ WebPartTitlePlaceHolder: string;
+ ModerAudienceEnabledTitle: string;
+ModernAudienceEnabledTitle: string;
}
declare module 'ListSearchWebPartStrings' {
diff --git a/samples/react-list-search/src/webparts/listSearch/model/IListConfigProps.ts b/samples/react-list-search/src/webparts/listSearch/model/IListConfigProps.ts
index cac418bbb..9ba3dc6b7 100644
--- a/samples/react-list-search/src/webparts/listSearch/model/IListConfigProps.ts
+++ b/samples/react-list-search/src/webparts/listSearch/model/IListConfigProps.ts
@@ -8,6 +8,7 @@ export interface IListData {
Query: string;
uniqueId: string;
sortIdx: number;
+ AudienceEnabled: boolean;
}
export interface IBaseFieldData {
diff --git a/samples/react-list-search/src/webparts/listSearch/model/IMapQuery.ts b/samples/react-list-search/src/webparts/listSearch/model/IMapQuery.ts
index 65d591644..5f2fb948c 100644
--- a/samples/react-list-search/src/webparts/listSearch/model/IMapQuery.ts
+++ b/samples/react-list-search/src/webparts/listSearch/model/IMapQuery.ts
@@ -2,17 +2,18 @@ import { SiteList } from "./IListConfigProps";
import { SharePointType } from "./ISharePointFieldTypes";
export interface IMapQuery {
- [site: string]: Array
;
+ [site: string]: Array;
}
export interface IMapQueryList {
- [list: string]: Array;
+ [list: string]: Array;
}
export interface IListSearchListQuery {
- list: SiteList;
- camlQuery?: string;
- viewName?: string;
- fields: Array<{ originalField: string, newField: string, fieldType: SharePointType }>;
- }
+ list: SiteList;
+ audienceEnabled: boolean;
+ camlQuery?: string;
+ viewName?: string;
+ fields: Array<{ originalField: string, newField: string, fieldType: SharePointType }>;
+}
diff --git a/samples/react-list-search/src/webparts/listSearch/model/IResult.ts b/samples/react-list-search/src/webparts/listSearch/model/IResult.ts
index 53bba1463..6b9779666 100644
--- a/samples/react-list-search/src/webparts/listSearch/model/IResult.ts
+++ b/samples/react-list-search/src/webparts/listSearch/model/IResult.ts
@@ -9,4 +9,9 @@ export default interface IResult {
UniqueId: string;
ServerUrl: string;
FileLeafRef: string;
-}
\ No newline at end of file
+ OData__ModernAudienceTargetUserField:Audience[];
+}
+
+interface Audience{
+ Name: string;
+}
diff --git a/samples/react-list-search/src/webparts/listSearch/services/GraphService.ts b/samples/react-list-search/src/webparts/listSearch/services/GraphService.ts
new file mode 100644
index 000000000..a11d4735a
--- /dev/null
+++ b/samples/react-list-search/src/webparts/listSearch/services/GraphService.ts
@@ -0,0 +1,37 @@
+import { graph } from "@pnp/graph";
+import "@pnp/graph/users";
+import "@pnp/graph/groups";
+import { WebPartContext } from "@microsoft/sp-webpart-base";
+import { PnPClientStorage } from "@pnp/common";
+import IGraphService from "./IGraphService";
+
+
+
+export default class GraphService implements IGraphService {
+
+ private _storage: PnPClientStorage;
+ private _localStorageKey: string = 'userGroups';
+ // 1 day
+ private _expiredTimeMillisecons: number = 8.64e+7;
+
+ public constructor(spfxContext: WebPartContext) {
+ graph.setup({
+ spfxContext
+ });
+ this._storage = new PnPClientStorage();
+ }
+
+ public async getTransitiveMemberOf(): Promise {
+ try {
+ this._storage.local.deleteExpired();
+ let userGroups = this._storage.local.get(this._localStorageKey);
+ if (!userGroups) {
+ userGroups = await graph.me.getMemberGroups();
+ this._storage.local.put(this._localStorageKey, userGroups, new Date(new Date().getTime() + this._expiredTimeMillisecons));
+ }
+ return userGroups;
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ }
+}
diff --git a/samples/react-list-search/src/webparts/listSearch/services/IGraphService.ts b/samples/react-list-search/src/webparts/listSearch/services/IGraphService.ts
new file mode 100644
index 000000000..031ad945b
--- /dev/null
+++ b/samples/react-list-search/src/webparts/listSearch/services/IGraphService.ts
@@ -0,0 +1,4 @@
+
+export default interface IGraphService {
+ getTransitiveMemberOf(): Promise>;
+}
diff --git a/samples/react-list-search/src/webparts/listSearch/services/IListService.ts b/samples/react-list-search/src/webparts/listSearch/services/IListService.ts
index 4d1a22ef6..854580540 100644
--- a/samples/react-list-search/src/webparts/listSearch/services/IListService.ts
+++ b/samples/react-list-search/src/webparts/listSearch/services/IListService.ts
@@ -1,8 +1,9 @@
import { ListField } from "../model/IListConfigProps";
import { IListSearchListQuery } from "../model/IMapQuery";
+import GraphService from "./GraphService";
export default interface IListService {
- getListItems(listQueryOptions: IListSearchListQuery, listPropertyName: string, sitePropertyName: string, sitePropertyValue: string, rowLimit: number): Promise>;
+ getListItems(listQueryOptions: IListSearchListQuery, listPropertyName: string, sitePropertyName: string, sitePropertyValue: string, rowLimit: number, graphService?: GraphService): Promise>;
getListItemById(listQueryOptions: IListSearchListQuery, itemId: number): Promise;
getSiteListsTitle(): Promise>;
getListFields(listTitle: string): Promise>;
diff --git a/samples/react-list-search/src/webparts/listSearch/services/ListService.ts b/samples/react-list-search/src/webparts/listSearch/services/ListService.ts
index 2f04f3638..4607f38b0 100644
--- a/samples/react-list-search/src/webparts/listSearch/services/ListService.ts
+++ b/samples/react-list-search/src/webparts/listSearch/services/ListService.ts
@@ -11,9 +11,10 @@ import XMLParser from 'react-xml-parser';
import { IWeb, Web } from '@pnp/sp/webs';
import { SharePointType } from '../model/ISharePointFieldTypes';
import IResult from '../model/IResult';
-import { isEmpty } from '@microsoft/sp-lodash-subset';
+import { intersection, isEmpty } from '@microsoft/sp-lodash-subset';
import { ListField, SiteList } from '../model/IListConfigProps';
import { IListSearchListQuery } from '../model/IMapQuery';
+import GraphService from './GraphService';
export interface QueryHelperEntity {
@@ -21,9 +22,12 @@ export interface QueryHelperEntity {
expandFields: string[];
}
+
export default class ListService implements IListService {
private web: IWeb;
private baseUrl: string;
+ private static SharePointOnlineAudienceOOTBFieldName = "OData__ModernAudienceTargetUserField";
+ public static MAX_TOP: number = 5000;
constructor(siteUrl: string, useCache: boolean, cacheTime?: number, cacheType?: "session" | "local") {
sp.setup({
@@ -40,7 +44,102 @@ export default class ListService implements IListService {
this.baseUrl = siteUrl;
}
- private GetViewFieldsWithId(listQueryOptions: IListSearchListQuery, isCamlQuery: boolean): QueryHelperEntity {
+ public async getListItems(listQueryOptions: IListSearchListQuery, listPropertyName: string, sitePropertyName: string, sitePropertyValue: string, rowLimit: number, graphService?: GraphService): Promise> {
+ try {
+ let camlQuery: boolean = false;
+ let items: any = undefined;
+ let queryConfig: QueryHelperEntity = this.GetViewFieldsWithId(listQueryOptions, !isEmpty(listQueryOptions.camlQuery) || !isEmpty(listQueryOptions.viewName), false);
+ if (listQueryOptions.camlQuery) {
+ let query = this.getCamlQueryWithViewFieldsAndRowLimit(listQueryOptions.camlQuery, queryConfig, rowLimit);
+ items = await this.getListItemsByCamlQuery(listQueryOptions.list.Id, query, queryConfig);
+ }
+ else {
+ if (listQueryOptions.viewName) {
+ let viewInfo: any = await this.web.lists.getById(listQueryOptions.list.Id).views.getByTitle(listQueryOptions.viewName).select("ViewQuery").get();
+ let query = this.getCamlQueryWithViewFieldsAndRowLimit(`${viewInfo.ViewQuery}`, queryConfig, rowLimit);
+ items = await this.getListItemsByCamlQuery(listQueryOptions.list.Id, query, queryConfig);
+ }
+ else {
+ items = await sp.web.lists.getById(listQueryOptions.list.Id).items
+ .select(queryConfig.viewFields.join(','))
+ .top(rowLimit || ListService.MAX_TOP)
+ .expand(queryConfig.expandFields.join(',')).usingCaching().get();
+ }
+ }
+
+ if (listQueryOptions.audienceEnabled && graphService) {
+ let userGroups: string[] = await graphService.getTransitiveMemberOf();
+ items = this.getAudienceItems(items, userGroups);
+ }
+
+ let mappedItems = items.map((i: IResult) => {
+ i.FileExtension = this.GetFileExtension(i.FileLeafRef);
+ i.SiteUrl = this.baseUrl;
+ i.ListName = listQueryOptions.list.Title;
+ i.List = listQueryOptions.list;
+
+ listQueryOptions.fields.map(field => {
+ i = this.GetItemValue(i, field, camlQuery);
+ });
+
+ if (listPropertyName) {
+ i[listPropertyName] = listQueryOptions.list.Title;
+ }
+ if (sitePropertyName) {
+ i[sitePropertyName] = sitePropertyValue;
+ }
+ return i;
+ });
+ return mappedItems;
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ }
+
+ public async getListItemById(listQueryOptions: IListSearchListQuery, itemId: number): Promise {
+ try {
+ let queryConfig: QueryHelperEntity = this.GetViewFieldsWithId(listQueryOptions, false, true);
+ return this.web.lists.getById(listQueryOptions.list.Id).items.getById(itemId).select(queryConfig.viewFields.join(',')).expand(queryConfig.expandFields.join(',')).usingCaching().get();
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ }
+
+ public async getSiteListsTitle(): Promise> {
+ try {
+ return this.web.lists.filter('Hidden eq false').select('Title,Id').get();
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ }
+
+ public async getListFields(listId: string): Promise> {
+ try {
+ return this.web.lists.getById(listId).fields.select('EntityPropertyName,Title,InternalName,TypeAsString').get();
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ }
+
+ private getAudienceItems(itemsToFilter: IResult[], userGroups: string[]) {
+ let results: IResult[] = [];
+ userGroups && itemsToFilter.map(item => {
+ let itemAudiencesIds: string[] = item.OData__ModernAudienceTargetUserField && item.OData__ModernAudienceTargetUserField.map(audience => { return audience.Name.split("|")[2]; });
+ if (itemAudiencesIds) {
+ let matches: string[] = intersection(itemAudiencesIds, userGroups);
+ if (matches && matches.length > 0) {
+ results.push(item);
+ }
+ }
+ else {
+ results.push(item);
+ }
+ });
+
+ return results;
+ }
+
+ private GetViewFieldsWithId(listQueryOptions: IListSearchListQuery, isCamlQuery: boolean, isItemId: boolean): QueryHelperEntity {
let result: QueryHelperEntity = { expandFields: [], viewFields: ['ServerUrl', 'FileLeafRef', 'Id', 'UniqueId'] };
let hasToAddFieldsAsText: boolean = false;
listQueryOptions.fields.map(field => {
@@ -100,6 +199,11 @@ export default class ListService implements IListService {
result.expandFields.push('FieldValuesAsText');
}
+ if (listQueryOptions.audienceEnabled && !isItemId) {
+ result.expandFields.push(ListService.SharePointOnlineAudienceOOTBFieldName);
+ result.viewFields.push(`${ListService.SharePointOnlineAudienceOOTBFieldName}/Name`);
+ }
+
return result;
}
@@ -186,90 +290,6 @@ export default class ListService implements IListService {
return item;
}
- public async getListItems(listQueryOptions: IListSearchListQuery, listPropertyName: string, sitePropertyName: string, sitePropertyValue: string, rowLimit: number): Promise> {
- try {
- let camlQuery: boolean = false;
- let items: any = undefined;
- let queryConfig: QueryHelperEntity = this.GetViewFieldsWithId(listQueryOptions, !isEmpty(listQueryOptions.camlQuery) || !isEmpty(listQueryOptions.viewName));
- if (listQueryOptions.camlQuery) {
- let query = this.getCamlQueryWithViewFieldsAndRowLimit(listQueryOptions.camlQuery, queryConfig, rowLimit);
- items = await this.getListItemsByCamlQuery(listQueryOptions.list.Id, query, queryConfig);
- }
- else {
- if (listQueryOptions.viewName) {
- let viewInfo: any = await this.web.lists.getById(listQueryOptions.list.Id).views.getByTitle(listQueryOptions.viewName).select("ViewQuery").get();
- let query = this.getCamlQueryWithViewFieldsAndRowLimit(`${viewInfo.ViewQuery}`, queryConfig, rowLimit);
- items = await this.getListItemsByCamlQuery(listQueryOptions.list.Id, query, queryConfig);
- }
- else {
- if (rowLimit) {
- if (queryConfig.expandFields && queryConfig.expandFields.length > 0) {
- items = await this.web.lists.getById(listQueryOptions.list.Id).items.select(queryConfig.viewFields.join(',')).expand(queryConfig.expandFields.join(',')).usingCaching().get();
- }
- else {
- items = await this.web.lists.getById(listQueryOptions.list.Id).items.top(rowLimit).select(queryConfig.viewFields.join(',')).usingCaching().get();
- }
- }
- else {
- if (queryConfig.expandFields && queryConfig.expandFields.length > 0) {
- items = await this.web.lists.getById(listQueryOptions.list.Id).items.select(queryConfig.viewFields.join(',')).expand(queryConfig.expandFields.join(',')).usingCaching().get();
- }
- else {
- items = await this.web.lists.getById(listQueryOptions.list.Id).items.select(queryConfig.viewFields.join(',')).usingCaching().get();
- }
- }
- }
-
- }
- let mappedItems = items.map((i: IResult) => {
- i.FileExtension = this.GetFileExtension(i.FileLeafRef);
- i.SiteUrl = this.baseUrl;
- i.ListName = listQueryOptions.list.Title;
- i.List = listQueryOptions.list;
-
- listQueryOptions.fields.map(field => {
- i = this.GetItemValue(i, field, camlQuery);
- });
-
- if (listPropertyName) {
- i[listPropertyName] = listQueryOptions.list.Title;
- }
- if (sitePropertyName) {
- i[sitePropertyName] = sitePropertyValue;
- }
- return i;
- });
- return mappedItems;
- } catch (error) {
- return Promise.reject(error);
- }
- }
-
- public async getListItemById(listQueryOptions: IListSearchListQuery, itemId: number): Promise {
- try {
- let queryConfig: QueryHelperEntity = this.GetViewFieldsWithId(listQueryOptions, false);
- return this.web.lists.getById(listQueryOptions.list.Id).items.getById(itemId).select(queryConfig.viewFields.join(',')).expand(queryConfig.expandFields.join(',')).usingCaching().get();
- } catch (error) {
- return Promise.reject(error);
- }
- }
-
- public async getSiteListsTitle(): Promise> {
- try {
- return this.web.lists.filter('Hidden eq false').select('Title,Id').get();
- } catch (error) {
- return Promise.reject(error);
- }
- }
-
- public async getListFields(listId: string): Promise> {
- try {
- return this.web.lists.getById(listId).fields.select('EntityPropertyName,Title,InternalName,TypeAsString').get();
- } catch (error) {
- return Promise.reject(error);
- }
- }
-
private async getListItemsByCamlQuery(listId: string, camlQuery: string, queryConfig: QueryHelperEntity): Promise> {
try {
const caml: ICamlQuery = {