Merge pull request #1464 from joaojmendes/master

updates
This commit is contained in:
Hugo Bernier 2020-08-29 16:15:36 -04:00 committed by GitHub
commit df87dd9cfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 300 additions and 160 deletions

View File

@ -25,6 +25,8 @@ Each Site has a symbol indicate if it is SharePoint Site, Group, Group and OnDri
If the Group has a microsoft team associated and user has permissions to access the symbol of teams will be displayed.
  
![MySites](./assets/MySites.gif)
![MySites](./assets/Screenshot%202020-08-06%20at%2013.50.51.png)
@ -56,6 +58,7 @@ react-my-sites|João Mendes
Version|Date|Comments
-------|----|--------
1.0.0|August 6, 2020|Initial release
1.0.1|August 29, 2020|Additional updates
## Disclaimer

View File

@ -3,7 +3,7 @@
"solution": {
"name": "react-my-sites-client-side-solution",
"id": "ad28b382-886b-4b2a-9646-92de8a0b1d13",
"version": "1.0.0.0",
"version": "1.0.1.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,

View File

@ -1,6 +1,6 @@
{
"name": "react-my-sites",
"version": "0.0.1",
"version": "1.0.1",
"private": true,
"main": "lib/index.js",
"engines": {

View File

@ -1,6 +1,7 @@
export enum Filters {
"SharePoint",
"All",
"Group",
"OneDrive",
"All",
"SharePoint",
"Site"
}

View File

@ -49,7 +49,8 @@ export const useUserSites = () => {
const getUserSites = async (
searchString?: string,
itemsPerPage?: number,
filter?: Filters
filter?: Filters,
site?:string
): Promise<SearchResults> => {
let searchResults: SearchResults = null;
let _filter: string = "";
@ -62,12 +63,17 @@ export const useUserSites = () => {
_filter = ` GroupId:a* OR GroupId:b* OR GroupId:c* OR GroupId:d* OR GroupId:e* OR GroupId:f* OR GroupId:g* OR GroupId:h* OR GroupId:i* OR GroupId:j* OR GroupId:k* OR GroupId:l* OR GroupId:m* OR GroupId:n* OR GroupId:o* OR GroupId:p* OR GroupId:q* OR GroupId:r* OR GroupId:s* OR GroupId:t* OR GroupId:u* OR GroupId:v* OR GroupId:w* OR GroupId:x* OR GroupId:y* OR GroupId:z* OR GroupId:1* OR GroupId:2* OR GroupId:3* OR GroupId:4* OR GroupId:5* OR GroupId:6* OR GroupId:7* OR GroupId:8* OR GroupId:9* OR GroupId:0*`;
break;
case Filters.OneDrive:
_filter = " SiteGroup:Onedrive";
_filter = " WebTemplate:SPSPERS"; // OneDrive
// _filter = " SiteGroup:Onedrive";
break;
case Filters.SharePoint:
_filter =
" SiteGroup:SharePoint AND NOT(GroupId:b* OR GroupId:c* OR GroupId:d* OR GroupId:e* OR GroupId:f* OR GroupId:g* OR GroupId:h* OR GroupId:i* OR GroupId:j* OR GroupId:k* OR GroupId:l* OR GroupId:m* OR GroupId:n* OR GroupId:o* OR GroupId:p* OR GroupId:q* OR GroupId:r* OR GroupId:s* OR GroupId:t* OR GroupId:u* OR GroupId:v* OR GroupId:w* OR GroupId:x* OR GroupId:y* OR GroupId:z* OR GroupId:1* OR GroupId:2* OR GroupId:3* OR GroupId:4* OR GroupId:5* OR GroupId:6* OR GroupId:7* OR GroupId:8* OR GroupId:9* OR GroupId:0*)";
break;
case Filters.Site:
_filter = `Path:${site}`;
break;
}
const q = SearchQueryBuilder(
@ -79,6 +85,11 @@ export const useUserSites = () => {
Direction: SortDirection.Descending,
})
.selectProperties(
"ParentLink",
"SPSiteURL",
"SiteID",
"SPWebUrl",
"WebId",
"SiteLogo",
"SiteClosed",
"RelatedHubSites",
@ -94,8 +105,10 @@ export const useUserSites = () => {
"ModifiedById",
"LastModifiedTime",
"OriginalPath",
"Path",
"Title",
"Created"
"Created",
"WebTemplate"
);
const results = await sp.search(q);
searchResults = results; // set the current results
@ -103,5 +116,47 @@ export const useUserSites = () => {
return searchResults;
};
return { getUserSites, checkGroupHasTeam };
// Get User Sites
const getUserWebs = async (
): Promise<SearchResults> => {
let searchResults: SearchResults = null;
const q = SearchQueryBuilder(
`(contentclass:STS_Web)`
)
.rowLimit(100000)
.selectProperties(
"ParentLink",
"SPSiteURL",
"SiteID",
"SPWebUrl",
"WebId",
"SiteLogo",
"SiteClosed",
"RelatedHubSites",
"IsHubSite",
"GroupId",
"RelatedGroupId",
"SiteGroup",
"Author",
"CreatedBy",
"CreatedById",
"AccountName",
"ModifiedBy",
"ModifiedById",
"LastModifiedTime",
"OriginalPath",
"Path",
"Title",
"Created",
"WebTemplate"
);
const results = await sp.search(q);
searchResults = results; // set the current results
console.log("webs",searchResults);
return searchResults;
};
return { getUserSites, checkGroupHasTeam, getUserWebs };
};

View File

@ -1,3 +1,4 @@
import { IContextualMenuProps } from "office-ui-fabric-react";
export interface IMySitesState {
sites: any[];
@ -9,4 +10,7 @@ export interface IMySitesState {
totalPages:number;
searchValue:string;
currentFilter?:number;
currentFilterName?:string;
currentSelectedSite?:string;
filterMenuProps: IContextualMenuProps;
}

View File

@ -1,5 +1,5 @@
import * as React from "react";
import { Filters} from '../../../../Entities/EnumFilters';
import { Filters } from "../../../../Entities/EnumFilters";
import "./paginationOverride.module.scss";
import { IMySitesProps } from "./IMySitesProps";
import { escape } from "@microsoft/sp-lodash-subset";
@ -18,6 +18,7 @@ import {
IContextualMenuItem,
FontIcon,
Label,
ContextualMenuItemType,
} from "office-ui-fabric-react";
import { WebPartTitle } from "@pnp/spfx-controls-react";
import { useUserSites } from "../../../../Hooks/useUserSites";
@ -31,7 +32,11 @@ import _ from "lodash";
import { MSGraphClient } from "@microsoft/sp-http";
let _searchResults: SearchResults = null;
let _msGraphClient:MSGraphClient = undefined;
let _msGraphClient: MSGraphClient = undefined;
let _filterMenuProps: IContextualMenuProps = undefined;
// Get Hook functions
const { getUserSites, getUserWebs } = useUserSites();
export const MySites: React.FunctionComponent<IMySitesProps> = (
props: IMySitesProps
@ -47,12 +52,11 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
},
webPartTile: {
fontWeight: 500,
marginBottom: 20
marginBottom: 20,
},
});
// Document Card Styles
// state
const [state, setState] = React.useState<IMySitesState>({
errorMessage: "",
@ -61,32 +65,40 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
hasError: false,
title: props.title,
currentPage: 1,
totalPages: 50,
totalPages: 0,
searchValue: "",
currentFilter: Filters.All,
currentFilterName: "All",
currentSelectedSite: undefined,
filterMenuProps: undefined,
});
const filterIcon: IIconProps = { iconName: "Filter" };
// Get Hook functions
const { getUserSites } = useUserSites();
// get User Sites
const _getUserSites = async (
searchString?: string,
currentFilter?: Filters
currentFilter?: Filters,
currentFilterName?:string,
site?:string
) => {
try {
console.log('tiles var', props.themeVariant);
setState({ ...state, isLoading: true });
const { itemsPerPage } = props;
const searchResults = await getUserSites(searchString, itemsPerPage, currentFilter);
const searchResults = await getUserSites(
searchString,
itemsPerPage,
currentFilter,
site
);
_searchResults = searchResults;
let _totalPages: number = searchResults.TotalRows / itemsPerPage;
const _modulus: number = searchResults.TotalRows % itemsPerPage;
_totalPages =
_modulus > 0 ? toInteger(_totalPages) + 1 : toInteger(_totalPages);
setState({
...state,
searchValue: "",
currentPage: 1,
totalPages: _totalPages,
@ -95,7 +107,11 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
hasError: false,
errorMessage: "",
sites: _searchResults.PrimarySearchResults,
currentFilter: currentFilter
currentFilter: currentFilter,
currentSelectedSite: site,
currentFilterName: currentFilterName,
// tslint:disable-next-line: no-use-before-declare
filterMenuProps: _filterMenuProps,
});
} catch (error) {
console.log(error);
@ -108,110 +124,154 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
}
};
const _Filtersites = async (filter: string) => {
setState({ ...state, isLoading: true });
let _filteredSites: any[] = [];
const _Filtersites = async (filter: string, site?:string) => {
switch (filter) {
case "All":
setState({...state,currentFilter: Filters.All});
await _getUserSites('', Filters.All);
await _getUserSites("", Filters.All, "All");
break;
case "Groups":
setState({...state,currentFilter: Filters.Group});
await _getUserSites('', Filters.Group);
await _getUserSites("", Filters.Group, "Groups");
break;
case "OneDrive":
setState({...state,currentFilter: Filters.OneDrive});
await _getUserSites('', Filters.OneDrive);
await _getUserSites("", Filters.OneDrive, "OneDrive");
break;
case "SharePoint":
setState({...state,currentFilter: Filters.SharePoint});
await _getUserSites('', Filters.SharePoint);
await _getUserSites("", Filters.SharePoint, "SharePoint");
break;
default:
setState({ ...state, isLoading: false });
break;
await _getUserSites("", Filters.Site, filter, site);
}
};
const filterMenuProps: IContextualMenuProps = {
items: [
{
key: "0",
text: "All",
iconProps: { iconName: "ThumbnailView" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
{
key: "1",
text: "SharePoint",
iconProps: { iconName: "SharepointAppIcon16" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
{
key: "2",
text: "Groups",
iconProps: { iconName: "Group" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
{
key: "3",
text: "OneDrive",
iconProps: { iconName: "onedrive" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
],
};
// useEffect component did mount or modified
React.useEffect(() => {
(async () => {
_msGraphClient = await props.context.msGraphClientFactory.getClient();
await _getUserSites("",state.currentFilter);
const _sitesWithSubSties = await getUserWebs();
console.log("subsites", _sitesWithSubSties);
const _uniqweb = _.uniqBy(
_sitesWithSubSties.PrimarySearchResults,
"ParentLink"
);
_filterMenuProps = {
items: [
{
key: "0",
text: "All",
iconProps: { iconName: "ThumbnailView" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
{
key: "1",
text: "SharePoint",
iconProps: { iconName: "SharepointAppIcon16" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
{
key: "2",
text: "Groups",
iconProps: { iconName: "Group" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
{
key: "3",
text: "OneDrive",
iconProps: { iconName: "onedrive" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
_Filtersites(item.text);
},
},
],
};
if (_sitesWithSubSties.PrimarySearchResults.length > 0){
_filterMenuProps.items.push(
{
key: 'sites',
itemType: ContextualMenuItemType.Header,
text: 'Sites with Sub Sites',
itemProps: {
lang: 'en-us',
},
}
);
}
// Add Site Collections with sub
for (const web of _uniqweb) {
// tslint:disable-next-line: no-use-before-declare
const _lastHasPosition: number = (web.ParentLink as string).lastIndexOf(
"/"
);
const _siteName: string = (web.ParentLink as string).substring(
_lastHasPosition + 1
);
// tslint:disable-next-line: no-use-before-declare
_filterMenuProps.items.push({
key: web.ParentLink,
text: _siteName,
iconProps: { iconName: "DrillExpand" },
onClick: (
ev:
| React.MouseEvent<HTMLElement, MouseEvent>
| React.KeyboardEvent<HTMLElement>,
item: IContextualMenuItem
) => {
// tslint:disable-next-line: no-use-before-declare
_Filtersites(item.text, item.key);
},
});
}
await _getUserSites("", state.currentFilter, state.currentFilterName);
})();
}, [props.title, props.itemsPerPage]);
// On Search Sites
const _onSearch = async (value: string) => {
await _getUserSites(value,state.currentFilter);
await _getUserSites(value, state.currentFilter,state.currentFilterName, state.currentSelectedSite);
};
// On Search Sites
const _onClear = async (ev: any) => {
await _getUserSites("",state.currentFilter);
await _getUserSites("", state.currentFilter,state.currentFilterName, state.currentSelectedSite);
};
// Render component
if (state.hasError) { // render message error
// Render component
if (state.hasError) {
// render message error
return (
<MessageBar messageBarType={MessageBarType.error}>
{state.errorMessage}
@ -229,9 +289,8 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
themeVariant={props.themeVariant}
updateProperty={props.updateProperty}
className={stylesComponent.webPartTile}
/>
<Stack horizontal horizontalAlign="end" tokens={{ childrenGap: 10 }}>
<Stack horizontal verticalAlign="center" horizontalAlign="end" wrap tokens={{ childrenGap: 5 }}>
<SearchBox
placeholder="Search my sites"
underlined={true}
@ -246,10 +305,11 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
/>
<CommandButton
iconProps={filterIcon}
text={Filters[state.currentFilter]}
menuProps={filterMenuProps}
text={state.currentFilterName}
menuProps={state.filterMenuProps}
disabled={false}
checked={true}
title="filter"
/>
</Stack>
{state.isLoading ? (
@ -259,29 +319,40 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
></Spinner>
) : (
<>
{ // has sites ?
state.sites.length > 0 ?
<div className={stylesComponent.containerTiles}>
{state.sites.map((site:any, i: number) => {
return (
<SiteTile
site={site}
msGraphClient={_msGraphClient}
themeVariant={props.themeVariant}
></SiteTile>
);
})}
</div>
:
<>
<Stack horizontal verticalAlign="center" horizontalAlign="center" tokens={{childrenGap: 20}} styles={{root: {marginTop: 50}}}>
<FontIcon iconName="Tiles" style={{fontSize: 48}}></FontIcon>
<Label styles={{root:{fontSize: 26}}}>No Sites Found </Label>
</Stack>
</>
}
{
// has sites ?
state.sites.length > 0 ? (
<div className={stylesComponent.containerTiles}>
{state.sites.map((site: any, i: number) => {
return (
<SiteTile
site={site}
msGraphClient={_msGraphClient}
themeVariant={props.themeVariant}
></SiteTile>
);
})}
</div>
) : (
<>
<Stack
horizontal
verticalAlign="center"
horizontalAlign="center"
tokens={{ childrenGap: 20 }}
styles={{ root: { marginTop: 50 } }}
>
<FontIcon
iconName="Tiles"
style={{ fontSize: 48 }}
></FontIcon>
<Label styles={{ root: { fontSize: 26 } }}>
No Sites Found{" "}
</Label>
</Stack>
</>
)
}
{state.totalPages > 1 && (
<>
@ -296,7 +367,7 @@ export const MySites: React.FunctionComponent<IMySitesProps> = (
color="primary"
count={state.totalPages}
page={state.currentPage}
onChange={async (event:any, page:number) => {
onChange={async (event: any, page: number) => {
const rs = await _searchResults.getPage(page);
_searchResults = rs;
setState({

View File

@ -94,10 +94,9 @@ export const SiteTile: React.FunctionComponent<ISiteTileProps> = (
fontSize: 20,
color: props.themeVariant ? props.themeVariant.palette.themePrimary: 'white',
marginTop: 8,
marginRight: 16,
marginRight: 7,
},
};
const DocumentCardActivityStyles: Partial<IDocumentCardActivityStyles> = {
root: { paddingBottom: 0 },
};
@ -107,7 +106,6 @@ export const SiteTile: React.FunctionComponent<ISiteTileProps> = (
};
let _activityUserEmail: string = "N/A";
let _activityUser: string = "N/A";
let _activityDate: string = "N/A";
@ -124,6 +122,8 @@ export const SiteTile: React.FunctionComponent<ISiteTileProps> = (
OriginalPath,
CreatedBy,
Created,
IsHubSite,
WebTemplate
} = props.site;
@ -208,7 +208,7 @@ export const SiteTile: React.FunctionComponent<ISiteTileProps> = (
previewImages={[
{
previewImageSrc:
SiteGroup == "OneDrive" ? _siteLogoOndrive : _siteLogoSP,
WebTemplate == "SPSPERS" ? _siteLogoOndrive : _siteLogoSP,
width: 68,
height: 68,
imageFit: ImageFit.cover,
@ -229,15 +229,22 @@ export const SiteTile: React.FunctionComponent<ISiteTileProps> = (
/>
)}
{GroupId && (
{GroupId && GroupId !== "00000000-0000-0000-0000-000000000000" && ( // (is groupId = undefined or 000000-0000-0000-0000000000000 guid) this is showned is some personal drives
<Icon
styles={groupIconStyles}
iconName="Group"
title="Office 365 Group"
></Icon>
)}
{SiteGroup == "OneDrive" && (
{IsHubSite == "true" && (
<Icon
styles={groupIconStyles}
iconName="DrillExpand"
title="is Hub Site"
></Icon>
)}
{WebTemplate == "SPSPERS" && (
<Icon
styles={groupIconStyles}
iconName="onedrive"

View File

@ -1,30 +1,29 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}