Merge pull request #2149 from aaclage/Feature/FollowDocumentGraph
updated to use Microsoft Graph follow
This commit is contained in:
commit
78ad673966
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
This solution has the goal to easily identify/follow user key documents from all Tenant and easily access them in Modern Pages and Microsoft Teams. This solution uses the Out of Box Social feature **"Follow Document WebPart"** with combination of MSGraph queries and extension for Microsoft Teams.
|
This solution has the goal to easily identify/follow user key documents from all Tenant and easily access them in Modern Pages and Microsoft Teams. This solution uses the Out of Box Office Page > Favorites Files Tab with combination of MSGraph queries and extension for Microsoft Teams.
|
||||||
|
|
||||||
This is a 2 phase project with associated dependency of solution [Follow-Document](https://github.com/pnp/sp-dev-fx-extensions/tree/main/samples/react-command-follow-document) extension where users are allow to select and manage Followed Document in Libraries to be used in this project.
|
This is a 2 phase project with associated dependency of solution [Follow-Document](https://github.com/pnp/sp-dev-fx-extensions/tree/main/samples/react-command-follow-document) extension where users are allow to select and manage Followed Document in Libraries to be used in this project.
|
||||||
|
|
||||||
|
@ -20,8 +20,9 @@ Available features:
|
||||||
- Microsoft Team integration with personal/Tab App that allow user focus on key Documents.
|
- Microsoft Team integration with personal/Tab App that allow user focus on key Documents.
|
||||||
|
|
||||||
Usage of following Technologies:
|
Usage of following Technologies:
|
||||||
- Usage of Social Feature **"Follow" documents** and associated REST "[/_api/social.following/](https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-follow-documents-sites-and-tags-by-using-the-rest-service-in-sharepoint-2)"
|
- Usage of Microsoft Graph API "[Follow drive item](https://docs.microsoft.com/en-us/graph/api/driveitem-follow?view=graph-rest-1.0&tabs=http)"
|
||||||
- Usage of Graph queries using [Graph explorer](https://developer.microsoft.com/en-us/graph/graph-explorer)
|
- Usage of Microsoft Graph API "[Unfollow drive item](https://docs.microsoft.com/en-us/graph/api/driveitem-unfollow?view=graph-rest-1.0&tabs=http)"
|
||||||
|
- Usage of Microsoft Graph API "[List followed items](https://docs.microsoft.com/en-us/graph/api/drive-list-following?view=graph-rest-1.0&tabs=http)"- Usage of Graph queries using [Graph explorer](https://developer.microsoft.com/en-us/graph/graph-explorer)
|
||||||
- Usage of [adaptive cards](https://adaptivecards.io/)
|
- Usage of [adaptive cards](https://adaptivecards.io/)
|
||||||
- Microsoft Teams integration with following option [TeamsTab, TeamsPersonalApp]
|
- Microsoft Teams integration with following option [TeamsTab, TeamsPersonalApp]
|
||||||
|
|
||||||
|
@ -57,6 +58,8 @@ o365 spo login https://contoso-admin.sharepoint.com
|
||||||
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Files.Read'
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Files.Read'
|
||||||
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Files.Read.All'
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Files.Read.All'
|
||||||
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Sites.Read.All'
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Sites.Read.All'
|
||||||
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Files.ReadWrite.All'
|
||||||
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Sites.ReadWrite.All'
|
||||||
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Team.ReadBasic.All'
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Team.ReadBasic.All'
|
||||||
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Channel.ReadBasic.All'
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'Channel.ReadBasic.All'
|
||||||
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'ChannelMessage.Send'
|
o365 spo serviceprincipal grant add --resource 'Microsoft Graph' --scope 'ChannelMessage.Send'
|
||||||
|
@ -73,7 +76,7 @@ react-follow-document | [André Lage](https://github.com/aaclage) (http://aaclag
|
||||||
Version|Date|Comments
|
Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
1.0|June 22, 2021|Initial release
|
1.0|June 22, 2021|Initial release
|
||||||
|
2.0|November 25, 2021|Change to use Microsoft Graph Follow
|
||||||
|
|
||||||
|
|
||||||
## Minimal Path to Awesome
|
## Minimal Path to Awesome
|
||||||
|
@ -93,7 +96,7 @@ Description of the extension that expands upon high-level summary above.
|
||||||
|
|
||||||
This extension illustrates the following concepts:
|
This extension illustrates the following concepts:
|
||||||
|
|
||||||
- Change of SharePoint Social Feature **"Follow"** to follow key Documents for users in Modern Sites.
|
- Usage of Office > Favorites to follow key Documents from users in Modern Sites.
|
||||||
- Simple UX to manage **Followed** documents and report list followed documents across Tenant and access properties and Preview of Document.
|
- Simple UX to manage **Followed** documents and report list followed documents across Tenant and access properties and Preview of Document.
|
||||||
- Option to unfollow documents individually.
|
- Option to unfollow documents individually.
|
||||||
- Integration with other services of Office 365 such us (Preview, Microsoft Team Messages).
|
- Integration with other services of Office 365 such us (Preview, Microsoft Team Messages).
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"identify/follow user key documents from all Tenant and easily access them in Modern Pages and Microsoft Teams"
|
"identify/follow user key documents from all Tenant and easily access them in Modern Pages and Microsoft Teams"
|
||||||
],
|
],
|
||||||
"creationDateTime": "2021-06-21",
|
"creationDateTime": "2021-06-21",
|
||||||
"updateDateTime": "2021-06-21",
|
"updateDateTime": "2021-11-25",
|
||||||
"products": [
|
"products": [
|
||||||
"SharePoint",
|
"SharePoint",
|
||||||
"Office"
|
"Office"
|
||||||
|
|
|
@ -14,25 +14,40 @@
|
||||||
"termsOfUseUrl": "",
|
"termsOfUseUrl": "",
|
||||||
"mpnId": ""
|
"mpnId": ""
|
||||||
},
|
},
|
||||||
"webApiPermissionRequests": [{
|
"webApiPermissionRequests": [
|
||||||
"resource": "Microsoft Graph",
|
{
|
||||||
"scope": "Files.Read"
|
"resource": "Microsoft Graph",
|
||||||
}, {
|
"scope": "Files.Read"
|
||||||
"resource": "Microsoft Graph",
|
},
|
||||||
"scope": "Files.Read.All"
|
{
|
||||||
}, {
|
"resource": "Microsoft Graph",
|
||||||
"resource": "Microsoft Graph",
|
"scope": "Files.ReadWrite.All"
|
||||||
"scope": "Sites.Read.All"
|
},
|
||||||
}, {
|
{
|
||||||
"resource": "Microsoft Graph",
|
"resource": "Microsoft Graph",
|
||||||
"scope": "Team.ReadBasic.All"
|
"scope": "Sites.ReadWrite.All"
|
||||||
}, {
|
},
|
||||||
"resource": "Microsoft Graph",
|
{
|
||||||
"scope": "Channel.ReadBasic.All"
|
"resource": "Microsoft Graph",
|
||||||
}, {
|
"scope": "Files.Read.All"
|
||||||
"resource": "Microsoft Graph",
|
},
|
||||||
"scope": "ChannelMessage.Send"
|
{
|
||||||
}]
|
"resource": "Microsoft Graph",
|
||||||
|
"scope": "Sites.Read.All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resource": "Microsoft Graph",
|
||||||
|
"scope": "Team.ReadBasic.All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resource": "Microsoft Graph",
|
||||||
|
"scope": "Channel.ReadBasic.All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resource": "Microsoft Graph",
|
||||||
|
"scope": "ChannelMessage.Send"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"zippedPackage": "solution/follow-document-web-part.sppkg"
|
"zippedPackage": "solution/follow-document-web-part.sppkg"
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
import {
|
|
||||||
SPHttpClient,
|
|
||||||
SPHttpClientResponse,
|
|
||||||
ISPHttpClientOptions,
|
|
||||||
} from "@microsoft/sp-http";
|
|
||||||
import * as MicrosoftGraph from "@microsoft/microsoft-graph-types";
|
|
||||||
|
|
||||||
export default class Rest {
|
|
||||||
public async isfollowed(
|
|
||||||
spHttpClient: SPHttpClient,
|
|
||||||
fileUrl: string,
|
|
||||||
siteUrl: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
const spOpts: ISPHttpClientOptions = {
|
|
||||||
headers: {
|
|
||||||
Accept: "application/json;odata.metadata=minimal",
|
|
||||||
"Content-type": "application/json;odata=verbose",
|
|
||||||
},
|
|
||||||
body: `{'actor': { 'ActorType':1, 'ContentUri':'${fileUrl}', 'Id':null}}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
const value = spHttpClient
|
|
||||||
.post(
|
|
||||||
`${siteUrl}/_api/social.following/isfollowed`,
|
|
||||||
SPHttpClient.configurations.v1,
|
|
||||||
spOpts
|
|
||||||
)
|
|
||||||
.then((response: SPHttpClientResponse): Promise<{
|
|
||||||
value: boolean;
|
|
||||||
}> => {
|
|
||||||
// Access properties of the response object.
|
|
||||||
console.log(`Status code: ${response.status}`);
|
|
||||||
console.log(`Status text: ${response.statusText}`);
|
|
||||||
|
|
||||||
//response.json() returns a promise so you get access to the json in the resolve callback.
|
|
||||||
return response.json();
|
|
||||||
/* response.json().then((responseJSON: JSON) => {
|
|
||||||
console.log(responseJSON);
|
|
||||||
});*/
|
|
||||||
})
|
|
||||||
.then((item: { value: boolean }) => {
|
|
||||||
return item.value;
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async follow(
|
|
||||||
spHttpClient: SPHttpClient,
|
|
||||||
fileUrl: string,
|
|
||||||
siteUrl: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
const spOpts: ISPHttpClientOptions = {
|
|
||||||
headers: {
|
|
||||||
Accept: "application/json;odata.metadata=minimal",
|
|
||||||
"Content-type": "application/json;odata=verbose",
|
|
||||||
},
|
|
||||||
body: `{'actor': { 'ActorType':1, 'ContentUri':'${fileUrl}', 'Id':null}}`,
|
|
||||||
};
|
|
||||||
const value = await spHttpClient
|
|
||||||
.post(
|
|
||||||
`${siteUrl}/_api/social.following/follow`,
|
|
||||||
SPHttpClient.configurations.v1,
|
|
||||||
spOpts
|
|
||||||
)
|
|
||||||
.then((response: SPHttpClientResponse): Promise<number> => {
|
|
||||||
// Access properties of the response object.
|
|
||||||
console.log(`Status code: ${response.status}`);
|
|
||||||
console.log(`Status text: ${response.statusText}`);
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((Item: any) => {
|
|
||||||
return Item.value;
|
|
||||||
});
|
|
||||||
if (value === 0) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async stopfollowing(
|
|
||||||
spHttpClient: SPHttpClient,
|
|
||||||
fileUrl: string,
|
|
||||||
siteUrl: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
const spOpts: ISPHttpClientOptions = {
|
|
||||||
headers: {
|
|
||||||
Accept: "application/json;odata.metadata=minimal",
|
|
||||||
"Content-type": "application/json;odata=verbose",
|
|
||||||
},
|
|
||||||
body: `{'actor': { 'ActorType':1, 'ContentUri':'${fileUrl}', 'Id':null}}`,
|
|
||||||
};
|
|
||||||
const value = await spHttpClient
|
|
||||||
.post(
|
|
||||||
`${siteUrl}/_api/social.following/stopfollowing`,
|
|
||||||
SPHttpClient.configurations.v1,
|
|
||||||
spOpts
|
|
||||||
)
|
|
||||||
.then((response: SPHttpClientResponse) => {
|
|
||||||
// Access properties of the response object.
|
|
||||||
console.log(`Status code: ${response.status}`);
|
|
||||||
console.log(`Status text: ${response.statusText}`);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
public async followed(
|
|
||||||
spHttpClient: SPHttpClient,
|
|
||||||
siteUrl: string
|
|
||||||
): Promise<MicrosoftGraph.DriveItem[]> {
|
|
||||||
const spOpts: ISPHttpClientOptions = {
|
|
||||||
headers: {
|
|
||||||
Accept: "application/json;odata.metadata=minimal",
|
|
||||||
"Content-type": "application/json;odata=verbose",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const values = spHttpClient
|
|
||||||
.post(
|
|
||||||
`${siteUrl}/_api/social.following/my/followed(types=2)`,
|
|
||||||
SPHttpClient.configurations.v1,
|
|
||||||
spOpts
|
|
||||||
)
|
|
||||||
.then((response: SPHttpClientResponse): Promise<
|
|
||||||
MicrosoftGraph.DriveItem[]
|
|
||||||
> => {
|
|
||||||
// Access properties of the response object.
|
|
||||||
console.log(`Status code: ${response.status}`);
|
|
||||||
console.log(`Status text: ${response.statusText}`);
|
|
||||||
|
|
||||||
//response.json() returns a promise so you get access to the json in the resolve callback.
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((Items:any) => {
|
|
||||||
let Values: MicrosoftGraph.DriveItem[] = [];
|
|
||||||
Items.value.forEach((element) => {
|
|
||||||
Values.push({
|
|
||||||
webUrl: decodeURIComponent(element.Uri),
|
|
||||||
name: element.Name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return Values;
|
|
||||||
});
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,8 +4,9 @@ import styles from './FollowDocumentWebPart.module.scss';
|
||||||
import { IFollowDocumentWebPartProps } from './IFollowDocumentWebPartProps';
|
import { IFollowDocumentWebPartProps } from './IFollowDocumentWebPartProps';
|
||||||
import { IFollowDocumentWebPartState } from './IFollowDocumentWebPartState';
|
import { IFollowDocumentWebPartState } from './IFollowDocumentWebPartState';
|
||||||
import { FollowDocumentGrid } from '../components/followDocumentGrid/index';
|
import { FollowDocumentGrid } from '../components/followDocumentGrid/index';
|
||||||
import Rest from '../Service/Rest';
|
|
||||||
import Graph from "../Service/GraphService";
|
import Graph from "../Service/GraphService";
|
||||||
|
import { FollowDocument } from "../models/followDocument";
|
||||||
|
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||||
|
|
||||||
// Used to render list grid
|
// Used to render list grid
|
||||||
import {
|
import {
|
||||||
|
@ -62,52 +63,228 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
visible: true,
|
visible: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//Load using Graph
|
let followDocuments: FollowDocument[] = [];
|
||||||
this.getGraphFollowedDocs();
|
this.getFollowDocuments(followDocuments).then((Items: FollowDocument[]) => {
|
||||||
|
//Order by Date
|
||||||
|
Items = Items.sort((a, b) => {
|
||||||
|
return b.followedDateTime.getTime() - a.followedDateTime.getTime();
|
||||||
|
});
|
||||||
|
let uniq = {};
|
||||||
|
let group: Array<IDropdownOption> = new Array<IDropdownOption>();
|
||||||
|
//Remove duplicated from array
|
||||||
|
let uniqueArray = [];
|
||||||
|
uniqueArray = Items.filter(obj => !uniq[obj.WebUrl] && (uniq[obj.WebUrl] = true));
|
||||||
|
group.push({ key: '0', text: 'All Sites' });
|
||||||
|
uniqueArray.forEach(Item => {
|
||||||
|
group.push({
|
||||||
|
key: Item.WebUrl,
|
||||||
|
text: "Site: " + Item.WebName,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
Items: Items,
|
||||||
|
ItemsSearch: Items,
|
||||||
|
ItemsGroup: group,
|
||||||
|
visible: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/********************************************************************** */
|
||||||
|
private getFollowDocuments = async (followDocuments: FollowDocument[]): Promise<any> => {
|
||||||
|
const graphService: Graph = new Graph();
|
||||||
|
let graphData: any = [];
|
||||||
|
graphData = await graphService.getGraphContent(`https://graph.microsoft.com/v1.0/me/drive/following?$select=id,name,webUrl,parentReference,followed&Top=1000`, this.props.context);
|
||||||
|
if (graphData.value !== undefined) {
|
||||||
|
graphData.value.forEach(data => {
|
||||||
|
|
||||||
|
let followDocument: FollowDocument = {
|
||||||
|
ItemId: data.id,
|
||||||
|
Title: data.name,
|
||||||
|
WebFileUrl: data.webUrl,
|
||||||
|
DriveId: data.parentReference.driveId,
|
||||||
|
followedDateTime: new Date(data.followed.followedDateTime),
|
||||||
|
} as FollowDocument;
|
||||||
|
this.GetIcon(data.name).then(icon => {
|
||||||
|
followDocument.IconUrl = (this.props.context.pageContext.web.absoluteUrl + "/_layouts/15/images/lg_" + icon).replace("lg_iczip.gif", "lg_iczip.png").replace("lg_icmsg.png", "lg_icmsg.gif");
|
||||||
|
});
|
||||||
|
followDocuments.push(followDocument);
|
||||||
|
});
|
||||||
|
followDocuments = await this.getList(followDocuments);
|
||||||
|
}
|
||||||
|
return followDocuments;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get Web Name and Web Url of Document
|
private getList = async (followDocuments: FollowDocument[]): Promise<any> => {
|
||||||
private getSearchWebID = async (graphData: any[], webs: any[]): Promise<any[]> => {
|
let items: FollowDocument[] = [];
|
||||||
const graphService: Graph = new Graph();
|
const graphService: Graph = new Graph();
|
||||||
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
||||||
let queryString: string = "";
|
if (initialized) {
|
||||||
for (let index = 0; index < webs.length; index++) {
|
let uniq = {};
|
||||||
if (index === 0) {
|
let uniqueArray = [];
|
||||||
queryString += "WebId:" + webs[index].replace('{', '').replace('}', '');
|
uniqueArray = followDocuments.filter(obj => !uniq[obj.DriveId] && (uniq[obj.DriveId] = true));
|
||||||
} else {
|
const requests = this.getBatchRequest(uniqueArray, "/me/drives/{driveId}/list?select=id,webUrl,parentReference", "GET");
|
||||||
queryString += " OR WebId:" + webs[index].replace('{', '').replace('}', '') + " ";
|
for (let index = 0; index < requests.length; index++) {
|
||||||
|
const graphData: any = await graphService.postGraphContent("https://graph.microsoft.com/v1.0/$batch", requests[index]);
|
||||||
|
graphData.responses.forEach((data: any) => {
|
||||||
|
followDocuments.forEach((followDocument: FollowDocument) => {
|
||||||
|
let driveId: string = decodeURI(data.body["@odata.context"].substring(
|
||||||
|
data.body["@odata.context"].indexOf("drives('") + 8,
|
||||||
|
data.body["@odata.context"].lastIndexOf("'")
|
||||||
|
));
|
||||||
|
if (followDocument.DriveId === driveId && (followDocument.ListId === undefined || followDocument.ListId === "")) {
|
||||||
|
followDocument.ListId = data.body.id;
|
||||||
|
followDocument.ItemProperties = data.body.webUrl + "/Forms/dispForm.aspx?ID=";
|
||||||
|
followDocument.SiteId = data.body.parentReference.siteId;
|
||||||
|
items.push(followDocument);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
followDocuments = await this.getDriveItem(items);
|
||||||
|
return followDocuments;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDriveItem = async (followDocuments: FollowDocument[]): Promise<any> => {
|
||||||
|
const graphService: Graph = new Graph();
|
||||||
|
let items: FollowDocument[] = [];
|
||||||
|
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
const HeaderWeb = {
|
const requests = this.getBatchRequest(followDocuments, "/me/drives/{driveId}/items/{ItemID}?$select=id,content.downloadUrl,ListItem&expand=ListItem(select=id,webUrl),thumbnails(select=large)", "GET");
|
||||||
"requests": [
|
for (let index = 0; index < requests.length; index++) {
|
||||||
{
|
const graphData: any = await graphService.postGraphContent("https://graph.microsoft.com/v1.0/$batch", requests[index]);
|
||||||
"entityTypes": [
|
graphData.responses.forEach((data: any) => {
|
||||||
"site"
|
followDocuments.forEach((followDocument: FollowDocument) => {
|
||||||
],
|
|
||||||
"query": {
|
if (followDocument.ItemId === data.body.id && followDocument.Url === undefined) {
|
||||||
"queryString": "" + queryString + "",
|
followDocument.id = data.body.listItem.id;
|
||||||
|
followDocument.Url = data.body.listItem.webUrl;
|
||||||
|
followDocument.Folder = data.body.listItem.webUrl.substring(0, data.body.listItem.webUrl.lastIndexOf("/") + 1);
|
||||||
|
followDocument.ItemProperties = followDocument.ItemProperties + data.body.listItem.id;
|
||||||
|
followDocument.DownloadFile = data.body["@microsoft.graph.downloadUrl"];
|
||||||
|
followDocument.Thumbnail = data.body.thumbnails.length > 0 ? data.body.thumbnails[0].large.url : "";
|
||||||
|
items.push(followDocument);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
]
|
});
|
||||||
};
|
}
|
||||||
//Retrieve webNames
|
followDocuments = await this.getWeb(items);
|
||||||
const tmpWebs = await graphService.postGraphContent("https://graph.microsoft.com/beta/search/query", HeaderWeb);
|
return followDocuments;
|
||||||
graphData.forEach(element => {
|
|
||||||
tmpWebs.value[0].hitsContainers[0].hits.forEach(Webelement => {
|
|
||||||
if (element.fields.WebId.replace('{', '').replace('}', '') === Webelement.resource.id.split(/[, ]+/).pop().toUpperCase()) {
|
|
||||||
element.WebName = Webelement.resource.name;
|
|
||||||
element.WebUrl = Webelement.resource.webUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return graphData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onActionTeamsClick = (action: any, ev: React.SyntheticEvent<HTMLElement>): void => {
|
private getWeb = async (followDocuments: FollowDocument[]): Promise<any> => {
|
||||||
|
const graphService: Graph = new Graph();
|
||||||
|
let items: FollowDocument[] = [];
|
||||||
|
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
||||||
|
if (initialized) {
|
||||||
|
let uniq = {};
|
||||||
|
let uniqueArray = [];
|
||||||
|
uniqueArray = followDocuments.filter(obj => !uniq[obj.SiteId] && (uniq[obj.SiteId] = true));
|
||||||
|
const requests = this.getBatchRequest(uniqueArray, "/sites/{SiteId}?$select=id,siteCollection,webUrl,name,displayName", "GET");
|
||||||
|
for (let index = 0; index < requests.length; index++) {
|
||||||
|
const graphData = await graphService.postGraphContent("https://graph.microsoft.com/v1.0/$batch", requests[index]);
|
||||||
|
graphData.responses.forEach((data: any) => {
|
||||||
|
followDocuments.forEach((followDocument: FollowDocument) => {
|
||||||
|
if (followDocument.SiteId === data.body.id && (followDocument.Domain === undefined || followDocument.Domain === "")) {
|
||||||
|
followDocument.Domain = data.body.siteCollection.hostname;
|
||||||
|
followDocument.WebUrl = data.body.webUrl;
|
||||||
|
followDocument.WebName = data.body.displayName;
|
||||||
|
followDocument.documentCardActions = [
|
||||||
|
{
|
||||||
|
iconProps: { iconName: 'TeamsLogo' },
|
||||||
|
onClick: this.onActionTeamsClick.bind(this, followDocument),
|
||||||
|
ariaLabel: 'Send to Teams',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconProps: { iconName: 'FabricFolder' },
|
||||||
|
onClick: this.onActionFolderClick.bind(this, followDocument),
|
||||||
|
ariaLabel: 'open Folder',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconProps: { iconName: 'FavoriteStarFill' },
|
||||||
|
onClick: this.onActionUnfollowClick.bind(this, followDocument),
|
||||||
|
ariaLabel: 'Unfollow Document',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconProps: { iconName: 'Info' },
|
||||||
|
onClick: this.onActionPropertiesClick.bind(this, followDocument),
|
||||||
|
ariaLabel: 'Document info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconProps: { iconName: 'DocumentSearch' },
|
||||||
|
onClick: this.onActionPanelClick.bind(this, followDocument),
|
||||||
|
ariaLabel: 'Preview',
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
items.push(followDocument);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetIcon = async (name: string): Promise<string> => {
|
||||||
|
var url = `${this.props.context.pageContext.web.absoluteUrl}/_api/web/maptoicon(filename='${name}',%20progid='',%20size=0)`;
|
||||||
|
const value = await this.props.context.spHttpClient.get(url, SPHttpClient.configurations.v1).then((response: SPHttpClientResponse): Promise<{
|
||||||
|
value: string;
|
||||||
|
}> => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((item: { value: string }) => {
|
||||||
|
return item.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBatchRequest = (followDocuments: FollowDocument[], graphQuery: string, method: string) => {
|
||||||
|
let HeaderDriveItemsId = {
|
||||||
|
"requests": []
|
||||||
|
};
|
||||||
|
let count = 1;
|
||||||
|
let Items = [];
|
||||||
|
followDocuments.forEach((element, index) => {
|
||||||
|
if (count < 21) {
|
||||||
|
HeaderDriveItemsId.requests.push({
|
||||||
|
"url": graphQuery.replace("{driveId}", element.DriveId).replace("{ItemID}", element.ItemId).replace("{SiteId}", element.SiteId),
|
||||||
|
"method": method,
|
||||||
|
"id": count
|
||||||
|
});
|
||||||
|
count++;
|
||||||
|
} else if (count === 21) {
|
||||||
|
Items.push(HeaderDriveItemsId);
|
||||||
|
HeaderDriveItemsId = {
|
||||||
|
"requests": []
|
||||||
|
};
|
||||||
|
count = 1;
|
||||||
|
HeaderDriveItemsId.requests.push({
|
||||||
|
"url": graphQuery.replace("{driveId}", element.DriveId).replace("{ItemID}", element.ItemId).replace("{SiteId}", element.SiteId),
|
||||||
|
"method": method,
|
||||||
|
"id": count
|
||||||
|
});
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (index === followDocuments.length - 1) {
|
||||||
|
Items.push(HeaderDriveItemsId);
|
||||||
|
HeaderDriveItemsId = {
|
||||||
|
"requests": []
|
||||||
|
};
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************* */
|
||||||
|
|
||||||
|
private onActionTeamsClick = (action: FollowDocument, ev: React.SyntheticEvent<HTMLElement>): void => {
|
||||||
|
|
||||||
const dialog: FollowDocumentDialog = new FollowDocumentDialog();
|
const dialog: FollowDocumentDialog = new FollowDocumentDialog();
|
||||||
dialog.initializedTeams(action, this.props.context, followType.SendTeams);
|
dialog.initializedTeams(action, this.props.context, followType.SendTeams);
|
||||||
|
@ -115,41 +292,12 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSearchListItemID = async (ListId: string): Promise<string> => {
|
private _showPanel = (followDocument: FollowDocument): void => {
|
||||||
const graphService: Graph = new Graph();
|
|
||||||
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
|
||||||
if (initialized) {
|
|
||||||
const HeaderListId = {
|
|
||||||
"requests": [
|
|
||||||
{
|
|
||||||
"entityTypes": [
|
|
||||||
"list"
|
|
||||||
],
|
|
||||||
"query": {
|
|
||||||
"queryString": "ListID:" + ListId + ""
|
|
||||||
},
|
|
||||||
"fields": [
|
|
||||||
"webUrl"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const tmpFileID = await graphService.postGraphContent("https://graph.microsoft.com/beta/search/query", HeaderListId);
|
|
||||||
console.log(tmpFileID);
|
|
||||||
return tmpFileID.value[0].hitsContainers[0].hits[0].resource.webUrl.substring(0, tmpFileID.value[0].hitsContainers[0].hits[0].resource.webUrl.lastIndexOf("/"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private getListItemID = async (ListID, ItemID) => {
|
|
||||||
const _ListId = await this.getSearchListItemID(ListID);
|
|
||||||
const dialog: FollowDocumentDialog = new FollowDocumentDialog();
|
|
||||||
dialog.initialize(_ListId + "/dispForm.aspx?ID=" + ItemID, followType.ViewPropreties);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _showPanel = (Url: string, Title: string): void => {
|
|
||||||
this._renderPanelComponent({
|
this._renderPanelComponent({
|
||||||
|
FollowDocument: followDocument,
|
||||||
context: this.props.context,
|
context: this.props.context,
|
||||||
url: Url,
|
url: followDocument.Url,
|
||||||
filename: Title,
|
filename: followDocument.Title,
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -159,15 +307,16 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
ReactDom.render(element, this._panelPlaceHolder);
|
ReactDom.render(element, this._panelPlaceHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onActionPropertiesClick = (action: any, ev: React.SyntheticEvent<HTMLElement>): void => {
|
private onActionPropertiesClick = (action: FollowDocument, ev: React.SyntheticEvent<HTMLElement>): void => {
|
||||||
//Get Document Display Form List
|
//Get Document Display Form List
|
||||||
this.getListItemID(action.fields.ListId.replace('{', '').replace('}', ''), action.fields.ItemId);
|
const dialog: FollowDocumentDialog = new FollowDocumentDialog();
|
||||||
|
dialog.initialize(action.ItemProperties, followType.ViewPropreties);
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onActionFolderClick = (action: any, ev: React.SyntheticEvent<HTMLElement>): void => {
|
private onActionFolderClick = (action: FollowDocument, ev: React.SyntheticEvent<HTMLElement>): void => {
|
||||||
window.open(action.fields.Url.replace(action.fields.Title, ""), "_blank");
|
window.open(action.Url.replace(action.Title, ""), "_blank");
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -175,159 +324,43 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
/**
|
/**
|
||||||
* Unfollow Option
|
* Unfollow Option
|
||||||
*/
|
*/
|
||||||
private onActionUnfollowClick = async (action: any, ev: React.SyntheticEvent<HTMLElement>) => {
|
private onActionUnfollowClick = async (action: FollowDocument, ev: React.SyntheticEvent<HTMLElement>) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const dialog: FollowDocumentDialog = new FollowDocumentDialog();
|
const dialog: FollowDocumentDialog = new FollowDocumentDialog();
|
||||||
dialog._followTypeDialog = followType.Unfollow;
|
dialog._followTypeDialog = followType.Unfollow;
|
||||||
dialog._filename = action.fields.Title;
|
dialog._filename = action.Title;
|
||||||
dialog.show().then(async () => {
|
dialog.show().then(async () => {
|
||||||
if (dialog._followDocumentState) {
|
if (dialog._followDocumentState) {
|
||||||
const restService: Rest = new Rest();
|
const graphService: Graph = new Graph();
|
||||||
const Status = await restService.stopfollowing(
|
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
||||||
this.props.context.spHttpClient,
|
if (initialized) {
|
||||||
action.fields.Url,
|
const graphData: any = await graphService.postGraphContent(`https://graph.microsoft.com/v1.0/drives/${action.DriveId}/items/${action.ItemId}/unfollow`, "");
|
||||||
this.props.context.pageContext.web.absoluteUrl,
|
if (graphData === undefined) {
|
||||||
);
|
dialog._followDocumentState = false;
|
||||||
if (Status) {
|
this.getListItems();
|
||||||
dialog._followDocumentState = false;
|
}
|
||||||
this.getListItems();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onActionPanelClick = async (action: any, ev: React.SyntheticEvent<HTMLElement>) => {
|
private onActionPanelClick = async (action: FollowDocument, ev: React.SyntheticEvent<HTMLElement>) => {
|
||||||
this._showPanel(action.fields.Url, action.fields.Title);
|
this._showPanel(action);
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGraphFollowedDocs = async () => {
|
|
||||||
const GraphService: Graph = new Graph();
|
|
||||||
let DriveItem: any = [];
|
|
||||||
|
|
||||||
if (this.state.siteId === null) {
|
|
||||||
let graphData: any = await GraphService.getGraphContent("https://graph.microsoft.com/v1.0/me/drive/list", this.props.context);
|
|
||||||
this._siteId = graphData.parentReference.siteId;
|
|
||||||
DriveItem = await this.getListID(graphData.parentReference.siteId);
|
|
||||||
} else {
|
|
||||||
if (this.state.listId === null) {
|
|
||||||
DriveItem = await this.getListID(this.state.siteId);
|
|
||||||
} else {
|
|
||||||
DriveItem = await this.getFollowDocuments(this.state.siteId, this.state.listId);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let items = [];
|
|
||||||
DriveItem.forEach(element => {
|
|
||||||
if (element.fields.IconUrl.indexOf("lg_iczip.gif") > -1) {
|
|
||||||
element.fields.IconUrl = element.fields.IconUrl.replace("lg_iczip.gif", "lg_iczip.png");
|
|
||||||
}
|
|
||||||
if (element.fields.IconUrl.indexOf("lg_icmsg.png") > -1) {
|
|
||||||
element.fields.IconUrl = element.fields.IconUrl.replace("lg_icmsg.png", "lg_icmsg.gif");
|
|
||||||
}
|
|
||||||
items.push({
|
|
||||||
thumbnail: element.previewImg,
|
|
||||||
title: element.fields.Title,
|
|
||||||
profileImageSrc: element.fields.IconUrl,
|
|
||||||
url: (element.fields.ServerUrlProgid === undefined ? element.fields.Url : element.fields.ServerUrlProgid.substring(1)),
|
|
||||||
webName: element.WebName,
|
|
||||||
webUrl: element.WebUrl,
|
|
||||||
documentCardActions: [
|
|
||||||
{
|
|
||||||
iconProps: { iconName: 'TeamsLogo' },
|
|
||||||
onClick: this.onActionTeamsClick.bind(this, element),
|
|
||||||
ariaLabel: 'Send to Teams',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconProps: { iconName: 'FabricFolder' },
|
|
||||||
onClick: this.onActionFolderClick.bind(this, element),
|
|
||||||
ariaLabel: 'open Folder',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconProps: { iconName: 'FavoriteStarFill' },
|
|
||||||
onClick: this.onActionUnfollowClick.bind(this, element),
|
|
||||||
ariaLabel: 'Unfollow Document',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconProps: { iconName: 'Info' },
|
|
||||||
onClick: this.onActionPropertiesClick.bind(this, element),
|
|
||||||
ariaLabel: 'Document info',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconProps: { iconName: 'DocumentSearch' },
|
|
||||||
onClick: this.onActionPanelClick.bind(this, element),
|
|
||||||
ariaLabel: 'Preview',
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
let uniq = {};
|
|
||||||
let group: Array<IDropdownOption> = new Array<IDropdownOption>();
|
|
||||||
//Remove duplicated from array
|
|
||||||
let uniqueArray = [];
|
|
||||||
uniqueArray = items.filter(obj => !uniq[obj.webUrl] && (uniq[obj.webUrl] = true));
|
|
||||||
group.push({ key: '0', text: 'All Sites' });
|
|
||||||
uniqueArray.forEach(element => {
|
|
||||||
group.push({
|
|
||||||
key: element.webUrl,
|
|
||||||
text: "Site: " + element.webName,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
Items: items,
|
|
||||||
ItemsSearch: items,
|
|
||||||
ItemsGroup: group,
|
|
||||||
visible: false,
|
|
||||||
siteId: this._siteId,
|
|
||||||
listId: this._listId
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
private getListID = async (siteId: string): Promise<string> => {
|
|
||||||
const GraphService: Graph = new Graph();
|
|
||||||
let graphData: any = await GraphService.getGraphContent(`https://graph.microsoft.com/v1.0/sites/${siteId}/lists?$select=id&$filter=displayName eq 'Social'`, this.props.context);
|
|
||||||
this._listId = graphData.value[0].id;
|
|
||||||
const DriveItem: string = await this.getFollowDocuments(siteId, graphData.value[0].id);
|
|
||||||
return DriveItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFollowDocuments = async (siteId: string, listId: string): Promise<any> => {
|
|
||||||
const GraphService: Graph = new Graph();
|
|
||||||
let graphData: any = [];
|
|
||||||
graphData = await GraphService.getGraphContent(`https://graph.microsoft.com/v1.0/sites/${siteId}/Lists/${listId}/items?expand=fields(select=ItemId,ListId,SiteId,webId,Title,Url,ServerUrlProgid,IconUrl,File_x0020_Type.progid)&$filter=fields/ItemId gt -1`, this.props.context);
|
|
||||||
graphData.value = graphData.value.sort((a, b) => {
|
|
||||||
return b.id - a.id;
|
|
||||||
});
|
|
||||||
|
|
||||||
//Get Web site Name
|
|
||||||
graphData = await this.getFollowDocumentsWebName(graphData);
|
|
||||||
return graphData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFollowDocumentsWebName = async (graphData) => {
|
|
||||||
let _webs = [];
|
|
||||||
graphData.value.forEach(element => {
|
|
||||||
if (_webs.indexOf(element.fields.WebId) === -1) {
|
|
||||||
_webs.push(element.fields.WebId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
graphData = await this.getSearchWebID(graphData.value, _webs);
|
|
||||||
return graphData;
|
|
||||||
}
|
|
||||||
public render(): React.ReactElement<IFollowDocumentWebPartProps> {
|
public render(): React.ReactElement<IFollowDocumentWebPartProps> {
|
||||||
//Filter Search Text
|
//Filter Search Text
|
||||||
const checkSearchDrive = (SearchQuery: string) => {
|
const checkSearchDrive = (SearchQuery: string) => {
|
||||||
let items = [];
|
let items = [];
|
||||||
if (this._selectedGroup === "0") {
|
if (this._selectedGroup === "0") {
|
||||||
items = this.state.Items.filter(item => (item.title.toLowerCase().indexOf(SearchQuery.toLowerCase()) > -1));
|
items = this.state.Items.filter(item => (item.Title.toLowerCase().indexOf(SearchQuery.toLowerCase()) > -1));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
items = this.state.Items.filter(item => (item.title.toLowerCase().indexOf(SearchQuery.toLowerCase()) > -1 && item.webUrl.toLowerCase().indexOf(this._selectedGroup.toLowerCase()) > -1));
|
items = this.state.Items.filter(item => (item.Title.toLowerCase().indexOf(SearchQuery.toLowerCase()) > -1 && item.WebUrl.toLowerCase().indexOf(this._selectedGroup.toLowerCase()) > -1));
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
ItemsSearch: items,
|
ItemsSearch: items,
|
||||||
|
@ -339,7 +372,7 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
items = this.state.Items;
|
items = this.state.Items;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
items = this.state.Items.filter(item => (item.webUrl.toLowerCase().indexOf(this._selectedGroup.toLowerCase()) > -1));
|
items = this.state.Items.filter(item => (item.WebUrl.toLowerCase().indexOf(this._selectedGroup.toLowerCase()) > -1));
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
ItemsSearch: items,
|
ItemsSearch: items,
|
||||||
|
@ -353,7 +386,7 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
ItemsSearch: this.state.Items,
|
ItemsSearch: this.state.Items,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const items = this.state.Items.filter(item => item.webUrl.toLowerCase().indexOf(selectedOption.key.toString().toLowerCase()) > -1);
|
const items = this.state.Items.filter(item => item.WebUrl.toLowerCase() === selectedOption.key.toString().toLowerCase());
|
||||||
this.setState({
|
this.setState({
|
||||||
ItemsSearch: items,
|
ItemsSearch: items,
|
||||||
});
|
});
|
||||||
|
@ -405,31 +438,26 @@ export default class FollowDocumentWebPart extends React.Component<IFollowDocume
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<FollowDocumentGrid
|
<FollowDocumentGrid
|
||||||
items={this.state.ItemsSearch}
|
items={this.state.ItemsSearch}
|
||||||
onRenderGridItem={(item: any, finalSize: ISize, isCompact: boolean) => this._onRenderGridItem(item, finalSize, isCompact)}
|
onRenderGridItem={(item, finalSize: ISize, isCompact: boolean) => this._onRenderGridItem(item, finalSize, isCompact)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private _onRenderGridItem = (item: any, finalSize: ISize, isCompact: boolean): JSX.Element => {
|
private _onRenderGridItem = (item: FollowDocument, finalSize: ISize, isCompact: boolean): JSX.Element => {
|
||||||
|
|
||||||
return <div
|
return <div className={styles.documentTile} data-is-focusable={true} aria-label={item.Title} >
|
||||||
className={styles.documentTile}
|
|
||||||
data-is-focusable={true}
|
|
||||||
role="listitem"
|
|
||||||
aria-label={item.title}
|
|
||||||
>
|
|
||||||
<DocumentCard
|
<DocumentCard
|
||||||
type={isCompact ? DocumentCardType.compact : DocumentCardType.normal}
|
type={isCompact ? DocumentCardType.compact : DocumentCardType.normal}
|
||||||
|
|
||||||
>
|
>
|
||||||
<div style={{ cursor: 'pointer' }} onClick={() => window.open(item.url, '_blank')}>
|
<div style={{ cursor: 'pointer' }} onClick={() => window.open(item.WebFileUrl, '_blank')}>
|
||||||
<DocumentCardImage height={100} imageFit={ImageFit.center} imageSrc={item.profileImageSrc} />
|
<DocumentCardImage height={100} imageFit={ImageFit.center} imageSrc={item.IconUrl} />
|
||||||
</div>
|
</div>
|
||||||
{!isCompact && <DocumentCardLocation location={item.webName} onClick={() => window.open(item.webUrl, '_blank')} />}
|
{!isCompact && <DocumentCardLocation location={item.WebName} onClick={() => window.open(item.WebUrl, '_blank')} />}
|
||||||
<DocumentCardDetails>
|
<DocumentCardDetails>
|
||||||
<DocumentCardTitle
|
<DocumentCardTitle
|
||||||
title={item.title}
|
title={item.Title}
|
||||||
shouldTruncate={true}
|
shouldTruncate={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||||
|
import { FollowDocument } from "../models/followDocument";
|
||||||
export interface IFollowDocumentWebPartState {
|
export interface IFollowDocumentWebPartState {
|
||||||
siteId?: string;
|
siteId?: string;
|
||||||
listId?: string;
|
listId?: string;
|
||||||
Items: any;
|
Items: FollowDocument[];
|
||||||
ItemsSearch?: any;
|
ItemsSearch?: any;
|
||||||
ItemsGroup?: IDropdownOption[];
|
ItemsGroup?: IDropdownOption[];
|
||||||
previewImgUrl:string;
|
previewImgUrl:string;
|
||||||
|
|
|
@ -8,14 +8,15 @@ import { BaseDialog, IDialogConfiguration } from '@microsoft/sp-dialog';
|
||||||
import { DialogContent, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
|
import { DialogContent, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
|
||||||
import { FollowDocumentProperties } from '../followDocumentProperties/followDocumentProperties';
|
import { FollowDocumentProperties } from '../followDocumentProperties/followDocumentProperties';
|
||||||
import { FollowDocumentSendMessage } from '../followDocumentSendMessage/followDocumentSendMessage';
|
import { FollowDocumentSendMessage } from '../followDocumentSendMessage/followDocumentSendMessage';
|
||||||
|
import { FollowDocument } from '../../models/followDocument';
|
||||||
|
|
||||||
export default class FollowDocumentDialog extends BaseDialog {
|
export default class FollowDocumentDialog extends BaseDialog {
|
||||||
public _followDocumentState: boolean = false;
|
public _followDocumentState: boolean = false;
|
||||||
private _webUrl: string;
|
private _webUrl: string;
|
||||||
public _filename:string;
|
public _filename: string;
|
||||||
private _context:WebPartContext;
|
private _context: WebPartContext;
|
||||||
public _followTypeDialog: followType;
|
public _followTypeDialog: followType;
|
||||||
public _fileInfo: any;
|
public _fileInfo: FollowDocument;
|
||||||
public return: (string) => void;
|
public return: (string) => void;
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ export default class FollowDocumentDialog extends BaseDialog {
|
||||||
this._followTypeDialog = type;
|
this._followTypeDialog = type;
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
public async initializedTeams(fileInfo: any,context:WebPartContext, type: followType) {
|
public async initializedTeams(fileInfo: FollowDocument, context: WebPartContext, type: followType) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._fileInfo = fileInfo;
|
this._fileInfo = fileInfo;
|
||||||
this._followTypeDialog = type;
|
this._followTypeDialog = type;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||||
|
import { FollowDocument } from "../../models/followDocument";
|
||||||
export interface IfollowDocumentPreviewProps {
|
export interface IfollowDocumentPreviewProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
url?:string;
|
url?:string;
|
||||||
filename?:string;
|
filename?:string;
|
||||||
context: WebPartContext;
|
context: WebPartContext;
|
||||||
visible?:boolean;
|
visible?:boolean;
|
||||||
|
FollowDocument: FollowDocument;
|
||||||
|
|
||||||
}
|
}
|
|
@ -33,21 +33,10 @@ export class followDocumentPreview extends React.Component<IfollowDocumentPrevie
|
||||||
const graphService: Graph = new Graph();
|
const graphService: Graph = new Graph();
|
||||||
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
const initialized = await graphService.initialize(this.props.context.serviceScope);
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
const HeaderItem = {
|
let graphData: any = await graphService.postGraphContent(`https://graph.microsoft.com/v1.0/drives/${this.props.FollowDocument.DriveId}/items/${this.props.FollowDocument.ItemId}/preview`, {});
|
||||||
"requests": [
|
|
||||||
{
|
|
||||||
"entityTypes": ["driveItem"],
|
|
||||||
"query": {
|
|
||||||
"queryString": "path:\"" + this.props.url.replace(this.props.filename, "") + "\" Filename:\"" + this.props.filename + "\"",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const tmpFileID = await graphService.postGraphContent("https://graph.microsoft.com/beta/search/query", HeaderItem);
|
|
||||||
let graphData: any = await graphService.postGraphContent(`https://graph.microsoft.com/v1.0/drives/${tmpFileID.value[0].hitsContainers[0].hits[0].resource.parentReference.driveId}/items/${tmpFileID.value[0].hitsContainers[0].hits[0].resource.id}/preview`, {});
|
|
||||||
this.setState({
|
this.setState({
|
||||||
preview: graphData.getUrl,
|
preview: graphData.getUrl,
|
||||||
name: tmpFileID.value[0].hitsContainers[0].hits[0].resource.name,
|
name: this.props.FollowDocument.Title,
|
||||||
visible: true,
|
visible: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||||
|
import { FollowDocument } from '../../models/followDocument';
|
||||||
export interface IfollowDocumentSendMessageProps {
|
export interface IfollowDocumentSendMessageProps {
|
||||||
close: () => void;
|
close: () => void;
|
||||||
url: string;
|
url: string;
|
||||||
context: WebPartContext;
|
context: WebPartContext;
|
||||||
fileInfo:any;
|
fileInfo: FollowDocument;
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ import { ITag, } from "office-ui-fabric-react/lib/Pickers";
|
||||||
import * as AdaptiveCards from "adaptivecards";
|
import * as AdaptiveCards from "adaptivecards";
|
||||||
import Graph from "../../Service/GraphService";
|
import Graph from "../../Service/GraphService";
|
||||||
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||||
|
import { FollowDocument } from '../../models/followDocument';
|
||||||
|
|
||||||
export class FollowDocumentSendMessage extends React.Component<IfollowDocumentSendMessageProps, IfollowDocumentSendMessageState> {
|
export class FollowDocumentSendMessage extends React.Component<IfollowDocumentSendMessageProps, IfollowDocumentSendMessageState> {
|
||||||
private card: any;
|
private card: any;
|
||||||
|
@ -47,7 +48,7 @@ export class FollowDocumentSendMessage extends React.Component<IfollowDocumentSe
|
||||||
const HeadersendMessage = {
|
const HeadersendMessage = {
|
||||||
"body": {
|
"body": {
|
||||||
"contentType": "html",
|
"contentType": "html",
|
||||||
"content": this._acContainer.innerHTML + `<a href="${(this.props.fileInfo.fields.ServerUrlProgid === undefined ? this.props.fileInfo.fields.Url : this.props.fileInfo.fields.ServerUrlProgid.substring(1))}">${this.props.fileInfo.fields.Title}</a>`
|
"content": this._acContainer.innerHTML + `<a href="${this.props.fileInfo.WebFileUrl}">${this.props.fileInfo.Title}</a>`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getresult = await graphService.postGraphContent(`https://graph.microsoft.com/v1.0/teams/${this.state.selectedTeamId}/channels/${this.state.selectedTeamChannelId}/messages`, HeadersendMessage);
|
const getresult = await graphService.postGraphContent(`https://graph.microsoft.com/v1.0/teams/${this.state.selectedTeamId}/channels/${this.state.selectedTeamChannelId}/messages`, HeadersendMessage);
|
||||||
|
@ -67,7 +68,7 @@ export class FollowDocumentSendMessage extends React.Component<IfollowDocumentSe
|
||||||
return getresult;
|
return getresult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public showAdaptiveCard(fileInfo: any) {
|
public showAdaptiveCard(fileInfo: FollowDocument) {
|
||||||
|
|
||||||
this.card = {
|
this.card = {
|
||||||
"type": "AdaptiveCard",
|
"type": "AdaptiveCard",
|
||||||
|
@ -86,7 +87,7 @@ export class FollowDocumentSendMessage extends React.Component<IfollowDocumentSe
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "Image",
|
"type": "Image",
|
||||||
"url": fileInfo.fields.IconUrl,
|
"url": fileInfo.IconUrl,
|
||||||
"size": "Small",
|
"size": "Small",
|
||||||
"spacing": "Medium"
|
"spacing": "Medium"
|
||||||
}
|
}
|
||||||
|
@ -99,7 +100,7 @@ export class FollowDocumentSendMessage extends React.Component<IfollowDocumentSe
|
||||||
{
|
{
|
||||||
"type": "TextBlock",
|
"type": "TextBlock",
|
||||||
"weight": "Bolder",
|
"weight": "Bolder",
|
||||||
"text": fileInfo.fields.Title,
|
"text": fileInfo.Title,
|
||||||
"wrap": true
|
"wrap": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
export interface FollowDocument {
|
||||||
|
id?: string;
|
||||||
|
Title?: string;
|
||||||
|
WebName?: string;
|
||||||
|
WebUrl?: string;
|
||||||
|
Domain?: string;
|
||||||
|
Folder?: string;
|
||||||
|
ItemProperties?: string;
|
||||||
|
WebFileUrl?: string;
|
||||||
|
DriveId?: string;
|
||||||
|
ListId?: string;
|
||||||
|
SiteId?: string;
|
||||||
|
ItemId?: string;
|
||||||
|
Url?: string;
|
||||||
|
DownloadFile?: string;
|
||||||
|
Thumbnail?: string;
|
||||||
|
IconUrl?: string;
|
||||||
|
documentCardActions?: Array<any>;
|
||||||
|
preview?: string;
|
||||||
|
Description?: string;
|
||||||
|
followedDateTime?: Date;
|
||||||
|
}
|
Loading…
Reference in New Issue