Added the 'My recent documents' sample (#21)
This commit is contained in:
parent
a3f68c4490
commit
ee4e6f4e8f
|
@ -16,6 +16,12 @@ Sample SharePoint Framework Client-Side Web Part built using React showing peopl
|
||||||
|
|
||||||
![Working with Web Part in the SharePoint Workbench](./assets/working-with-preview.png)
|
![Working with Web Part in the SharePoint Workbench](./assets/working-with-preview.png)
|
||||||
|
|
||||||
|
### My recent documents
|
||||||
|
|
||||||
|
Sample SharePoint Framework Client-Side Web Part built using React showing documents recently viewed or modified by the current user.
|
||||||
|
|
||||||
|
![Working with Web Part in the SharePoint Workbench](./assets/my-recent-documents-preview.png)
|
||||||
|
|
||||||
## Applies to
|
## Applies to
|
||||||
|
|
||||||
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
||||||
|
@ -31,6 +37,7 @@ react-officegraph|Waldek Mastykarz (MVP, Rencore, @waldekm)
|
||||||
|
|
||||||
Version|Date|Comments
|
Version|Date|Comments
|
||||||
-------|----|--------
|
-------|----|--------
|
||||||
|
1.2.0|September 20, 2016|Added the My recent documents sample
|
||||||
1.1.0|September 19, 2016|Added the Working with sample
|
1.1.0|September 19, 2016|Added the Working with sample
|
||||||
1.0.0|September 9, 2016|Initial release
|
1.0.0|September 9, 2016|Initial release
|
||||||
|
|
||||||
|
@ -56,24 +63,7 @@ Version|Date|Comments
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Trending in this site Web Part
|
Sample Web Parts in this solution illustrate the following concepts on top of the SharePoint Framework:
|
||||||
|
|
||||||
The _Trending in this site_ Client-Side Web Part is built on the SharePoint Framework using React and uses the [Office UI Fabric React](https://github.com/OfficeDev/office-ui-fabric-react) for showing document cards.
|
|
||||||
|
|
||||||
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
|
||||||
|
|
||||||
- using React for building SharePoint Framework Client-Side Web Parts
|
|
||||||
- using Office UI Fabric React components for building user experience consistent with SharePoint and Office
|
|
||||||
- communicating with SharePoint using its REST API
|
|
||||||
- communicating with the Office Graph via the SharePoint Search REST API
|
|
||||||
- passing Web Part properties to React components
|
|
||||||
- using ES6 Promises with vanilla-JavaScript web requests
|
|
||||||
|
|
||||||
### Working with Web Part
|
|
||||||
|
|
||||||
The _Working with_ Client-Side Web Part is built on the SharePoint Framework using React and uses the [Office UI Fabric React](https://github.com/OfficeDev/office-ui-fabric-react) to show persona cards.
|
|
||||||
|
|
||||||
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
|
||||||
|
|
||||||
- using React for building SharePoint Framework Client-Side Web Parts
|
- using React for building SharePoint Framework Client-Side Web Parts
|
||||||
- using Office UI Fabric React components for building user experience consistent with SharePoint and Office
|
- using Office UI Fabric React components for building user experience consistent with SharePoint and Office
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 445 KiB |
|
@ -9,6 +9,11 @@
|
||||||
"entry": "./lib/webparts/workingWith/WorkingWithWebPart.js",
|
"entry": "./lib/webparts/workingWith/WorkingWithWebPart.js",
|
||||||
"manifest": "./src/webparts/workingWith/WorkingWithWebPart.manifest.json",
|
"manifest": "./src/webparts/workingWith/WorkingWithWebPart.manifest.json",
|
||||||
"outputPath": "./dist/working-with.bundle.js"
|
"outputPath": "./dist/working-with.bundle.js"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entry": "./lib/webparts/myRecentDocuments/MyRecentDocumentsWebPart.js",
|
||||||
|
"manifest": "./src/webparts/myRecentDocuments/MyRecentDocumentsWebPart.manifest.json",
|
||||||
|
"outputPath": "./dist/my-recent-documents.bundle.js"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"externals": {
|
"externals": {
|
||||||
|
@ -22,6 +27,7 @@
|
||||||
},
|
},
|
||||||
"localizedResources": {
|
"localizedResources": {
|
||||||
"trendingInThisSiteStrings": "webparts/trendingInThisSite/loc/{locale}.js",
|
"trendingInThisSiteStrings": "webparts/trendingInThisSite/loc/{locale}.js",
|
||||||
"workingWithStrings": "webparts/workingWith/loc/{locale}.js"
|
"workingWithStrings": "webparts/workingWith/loc/{locale}.js",
|
||||||
|
"myRecentDocumentsStrings": "webparts/myRecentDocuments/loc/{locale}.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "react-officegraph",
|
"name": "react-officegraph",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export interface IActivity {
|
||||||
|
name: string;
|
||||||
|
date: string;
|
||||||
|
actorId: number;
|
||||||
|
actorName?: string;
|
||||||
|
actorPhotoUrl?: string;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IActorInformation {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
photoUrl: string;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { IActivity } from './IActivity';
|
||||||
|
|
||||||
|
export interface ITrendingDocument {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
url: string;
|
||||||
|
previewImageUrl: string;
|
||||||
|
extension: string;
|
||||||
|
activity: IActivity;
|
||||||
|
}
|
|
@ -25,9 +25,24 @@ export interface ICell {
|
||||||
ValueType: string;
|
ValueType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEdge {
|
||||||
|
ActorId: number;
|
||||||
|
ObjectId: number;
|
||||||
|
Properties: IEdgeProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEdgeProperties {
|
||||||
|
Action: number;
|
||||||
|
Blob: any[];
|
||||||
|
BlobContent: string;
|
||||||
|
ObjectSource: number;
|
||||||
|
Time: string;
|
||||||
|
Weight: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class SearchUtils {
|
export class SearchUtils {
|
||||||
public static getValueFromResults(key: string, results: ICell[]): string {
|
public static getValueFromResults(key: string, results: ICell[]): string {
|
||||||
let value: string = '';
|
let value: string = undefined;
|
||||||
|
|
||||||
if (results && results.length > 0 && key) {
|
if (results && results.length > 0 && key) {
|
||||||
for (let i: number = 0; i < results.length; i++) {
|
for (let i: number = 0; i < results.length; i++) {
|
||||||
|
@ -41,4 +56,29 @@ export class SearchUtils {
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getPreviewImageUrl(result: ICell[], siteUrl: string): string {
|
||||||
|
const uniqueID: string = SearchUtils.getValueFromResults('uniqueID', result);
|
||||||
|
const siteId: string = SearchUtils.getValueFromResults('siteID', result);
|
||||||
|
const webId: string = SearchUtils.getValueFromResults('webID', result);
|
||||||
|
const docId: string = SearchUtils.getValueFromResults('DocId', result);
|
||||||
|
if (uniqueID && siteId && webId && docId) {
|
||||||
|
return `${siteUrl}/_layouts/15/getpreview.ashx?guidFile=${uniqueID}&guidSite=${siteId}&guidWeb=${webId}&docid=${docId}
|
||||||
|
&metadatatoken=300x424x2&ClientType=CodenameOsloWeb&size=small`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getActionName(actionId: number): string {
|
||||||
|
switch (actionId) {
|
||||||
|
case 1001:
|
||||||
|
return 'Viewed';
|
||||||
|
case 1003:
|
||||||
|
return 'Modified';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,4 +2,13 @@ export class Utils {
|
||||||
public static getUserPhotoUrl(userEmail: string, siteUrl: string, size: string = 'S'): string {
|
public static getUserPhotoUrl(userEmail: string, siteUrl: string, size: string = 'S'): string {
|
||||||
return `${siteUrl}/_layouts/15/userphoto.aspx?size=${size}&accountname=${userEmail}`;
|
return `${siteUrl}/_layouts/15/userphoto.aspx?size=${size}&accountname=${userEmail}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static trim(s: string): string {
|
||||||
|
if (s && s.length > 0) {
|
||||||
|
return s.replace(/^\s+|\s+$/gm, '');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface IMyRecentDocumentsWebPartProps {
|
||||||
|
numberOfDocuments: number;
|
||||||
|
title: string;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
.myRecentDocuments {
|
||||||
|
.webPartTitle {
|
||||||
|
margin-bottom: 0.7em;
|
||||||
|
margin-left: 0.38em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .ms-DocumentCard {
|
||||||
|
float: left;
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global .ms-Spinner {
|
||||||
|
width: 7em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
|
||||||
|
|
||||||
|
"id": "e978c000-4905-4f25-8f5d-db250dbd30a6",
|
||||||
|
"componentType": "WebPart",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"manifestVersion": 2,
|
||||||
|
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "e978c000-4905-4f25-8f5d-db250dbd30a6",
|
||||||
|
"group": { "default": "Content rollup" },
|
||||||
|
"title": { "default": "My recent documents" },
|
||||||
|
"description": { "default": "Shows documents recently viewed or modified by the current user" },
|
||||||
|
"officeFabricIconFontName": "Recent",
|
||||||
|
"properties": {
|
||||||
|
"title": "My recent documents",
|
||||||
|
"numberOfDocuments": 5
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import {
|
||||||
|
BaseClientSideWebPart,
|
||||||
|
IPropertyPaneSettings,
|
||||||
|
IWebPartContext,
|
||||||
|
PropertyPaneSlider,
|
||||||
|
PropertyPaneTextField
|
||||||
|
} from '@microsoft/sp-client-preview';
|
||||||
|
|
||||||
|
import * as strings from 'myRecentDocumentsStrings';
|
||||||
|
import MyRecentDocuments, { IMyRecentDocumentsProps } from './components/MyRecentDocuments';
|
||||||
|
import { IMyRecentDocumentsWebPartProps } from './IMyRecentDocumentsWebPartProps';
|
||||||
|
|
||||||
|
export default class MyRecentDocumentsWebPart extends BaseClientSideWebPart<IMyRecentDocumentsWebPartProps> {
|
||||||
|
|
||||||
|
public constructor(context: IWebPartContext) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
const element: React.ReactElement<IMyRecentDocumentsProps> = React.createElement(MyRecentDocuments, {
|
||||||
|
numberOfDocuments: this.properties.numberOfDocuments,
|
||||||
|
title: this.properties.title,
|
||||||
|
httpClient: this.context.httpClient,
|
||||||
|
siteUrl: this.context.pageContext.web.absoluteUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get propertyPaneSettings(): IPropertyPaneSettings {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: strings.ViewGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('title', {
|
||||||
|
label: strings.TitleFieldLabel
|
||||||
|
}),
|
||||||
|
PropertyPaneSlider('numberOfDocuments', {
|
||||||
|
label: strings.NumberOfDocumentsFieldLabel,
|
||||||
|
min: 1,
|
||||||
|
max: 10,
|
||||||
|
step: 1
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
DocumentCard,
|
||||||
|
DocumentCardPreview,
|
||||||
|
DocumentCardTitle,
|
||||||
|
DocumentCardActivity,
|
||||||
|
Spinner
|
||||||
|
} from 'office-ui-fabric-react';
|
||||||
|
|
||||||
|
import styles from '../MyRecentDocuments.module.scss';
|
||||||
|
import { IMyRecentDocumentsWebPartProps } from '../IMyRecentDocumentsWebPartProps';
|
||||||
|
import { HttpClient } from '@microsoft/sp-client-base';
|
||||||
|
import { ITrendingDocument } from '../../ITrendingDocument';
|
||||||
|
import { IActorInformation } from '../../IActorInformation';
|
||||||
|
import { SearchUtils, ISearchQueryResponse, IRow, ICell, IEdge } from '../../SearchUtils';
|
||||||
|
import { Utils } from '../../Utils';
|
||||||
|
|
||||||
|
export interface IMyRecentDocumentsProps extends IMyRecentDocumentsWebPartProps {
|
||||||
|
httpClient: HttpClient;
|
||||||
|
siteUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMyRecentDocumentsState {
|
||||||
|
myDocuments: ITrendingDocument[];
|
||||||
|
loading: boolean;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MyRecentDocuments extends React.Component<IMyRecentDocumentsProps, IMyRecentDocumentsState> {
|
||||||
|
constructor(props: IMyRecentDocumentsProps, state: IMyRecentDocumentsState) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
myDocuments: [] as ITrendingDocument[],
|
||||||
|
loading: true,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.loadMyDocuments(this.props.siteUrl, this.props.numberOfDocuments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate(prevProps: IMyRecentDocumentsProps, prevState: IMyRecentDocumentsState, prevContext: any): void {
|
||||||
|
if (this.props.numberOfDocuments !== prevProps.numberOfDocuments ||
|
||||||
|
this.props.siteUrl !== prevProps.siteUrl && (
|
||||||
|
this.props.numberOfDocuments && this.props.siteUrl
|
||||||
|
)) {
|
||||||
|
this.loadMyDocuments(this.props.siteUrl, this.props.numberOfDocuments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const loading: JSX.Element = this.state.loading ? <div style={{ margin: '0 auto' }}><Spinner label={'Loading...'} /></div> : <div/>;
|
||||||
|
const error: JSX.Element = this.state.error ? <div><strong>Error: </strong> {this.state.error}</div> : <div/>;
|
||||||
|
const documents: JSX.Element[] = this.state.myDocuments.map((doc: ITrendingDocument, i: number) => {
|
||||||
|
const iconUrl: string = `https://spoprod-a.akamaihd.net/files/odsp-next-prod_ship-2016-08-15_20160815.002/odsp-media/images/filetypes/32/${doc.extension}.png`;
|
||||||
|
return (
|
||||||
|
<DocumentCard onClickHref={doc.url} key={doc.id}>
|
||||||
|
<DocumentCardPreview
|
||||||
|
previewImages={[
|
||||||
|
{
|
||||||
|
previewImageSrc: doc.previewImageUrl,
|
||||||
|
iconSrc: iconUrl,
|
||||||
|
width: 318,
|
||||||
|
height: 196,
|
||||||
|
accentColor: '#ce4b1f'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<DocumentCardTitle title={doc.title}/>
|
||||||
|
<DocumentCardActivity
|
||||||
|
activity={`${doc.activity.name} ${doc.activity.date}`}
|
||||||
|
people={
|
||||||
|
[
|
||||||
|
{ name: doc.activity.actorName, profileImageSrc: doc.activity.actorPhotoUrl }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</DocumentCard>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className={styles.myRecentDocuments}>
|
||||||
|
<div className={css('ms-font-xl', styles.webPartTitle)}>{this.props.title}</div>
|
||||||
|
{loading}
|
||||||
|
{error}
|
||||||
|
{documents}
|
||||||
|
<div style={{ clear: 'both' }}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadMyDocuments(siteUrl: string, numberOfDocuments: number): void {
|
||||||
|
const myDocuments: ITrendingDocument[] = [];
|
||||||
|
this.props.httpClient.get(`${siteUrl}/_api/search/query?querytext='*'&properties='GraphQuery:actor(me\\,or(action\\:1001\\,action\\:1003)),GraphRankingModel:{"features"\\:[{"function"\\:"EdgeTime"}]}'&selectproperties='Author,AuthorOwsUser,DocId,DocumentPreviewMetadata,Edges,EditorOwsUser,FileExtension,FileType,HitHighlightedProperties,HitHighlightedSummary,LastModifiedTime,LikeCountLifetime,ListID,ListItemID,OriginalPath,Path,Rank,SPWebUrl,SecondaryFileExtension,ServerRedirectedURL,SiteTitle,Title,ViewCountLifetime,siteID,uniqueID,webID'&rowlimit=${numberOfDocuments}&ClientType='MyRecentDocuments'&RankingModelId='0c77ded8-c3ef-466d-929d-905670ea1d72'`, {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json;odata=nometadata',
|
||||||
|
'odata-version': ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response: Response): Promise<ISearchQueryResponse> => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((response: ISearchQueryResponse): Promise<IActorInformation> => {
|
||||||
|
if (!response ||
|
||||||
|
!response.PrimaryQueryResult ||
|
||||||
|
!response.PrimaryQueryResult.RelevantResults ||
|
||||||
|
response.PrimaryQueryResult.RelevantResults.RowCount === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let actorId: number = undefined;
|
||||||
|
for (let i: number = 0; i < response.PrimaryQueryResult.RelevantResults.Table.Rows.length; i++) {
|
||||||
|
const row: IRow = response.PrimaryQueryResult.RelevantResults.Table.Rows[i];
|
||||||
|
const edges: IEdge[] = JSON.parse(SearchUtils.getValueFromResults('Edges', row.Cells));
|
||||||
|
if (edges.length < 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can get multiple edges back so let's show the information from the latest one
|
||||||
|
let latestEdge: IEdge = edges[0];
|
||||||
|
if (edges.length > 1) {
|
||||||
|
let latestEdgeDate: Date = new Date(latestEdge.Properties.Time);
|
||||||
|
for (let i: number = 1; i < edges.length; i++) {
|
||||||
|
const edgeDate: Date = new Date(edges[i].Properties.Time);
|
||||||
|
if (edgeDate > latestEdgeDate) {
|
||||||
|
latestEdge = edges[i];
|
||||||
|
latestEdgeDate = edgeDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actorId) {
|
||||||
|
// since all edges that we're retrieving are personal (I viewed, I modified)
|
||||||
|
// we only need to get the actor ID once because it's the same on all edges (me)
|
||||||
|
actorId = latestEdge.ActorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cells: ICell[] = row.Cells;
|
||||||
|
const date: Date = new Date(latestEdge.Properties.Time);
|
||||||
|
const dateString: string = (date.getMonth() + 1) + '/' + date.getDate() + '/' + date.getFullYear();
|
||||||
|
myDocuments.push({
|
||||||
|
id: SearchUtils.getValueFromResults('DocId', cells),
|
||||||
|
url: SearchUtils.getValueFromResults('ServerRedirectedURL', cells),
|
||||||
|
title: SearchUtils.getValueFromResults('Title', cells),
|
||||||
|
previewImageUrl: SearchUtils.getPreviewImageUrl(cells, siteUrl),
|
||||||
|
extension: SearchUtils.getValueFromResults('FileType', cells),
|
||||||
|
activity: {
|
||||||
|
actorId: latestEdge.ActorId,
|
||||||
|
date: dateString,
|
||||||
|
name: SearchUtils.getActionName(latestEdge.Properties.Action)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getActorsInfo(actorId, siteUrl);
|
||||||
|
}).
|
||||||
|
then((actorInformation: IActorInformation): void => {
|
||||||
|
if (actorInformation) {
|
||||||
|
for (let i: number = 0; i < myDocuments.length; i++) {
|
||||||
|
if (myDocuments[i].activity.actorId !== actorInformation.id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
myDocuments[i].activity.actorName = actorInformation.name;
|
||||||
|
myDocuments[i].activity.actorPhotoUrl = actorInformation.photoUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
myDocuments: myDocuments
|
||||||
|
});
|
||||||
|
}, (error: any): void => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
error: error,
|
||||||
|
myDocuments: []
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getActorsInfo(actorId: number, siteUrl: string): Promise<IActorInformation> {
|
||||||
|
if (!actorId) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve: (actorInformation: IActorInformation) => void, reject: (error: any) => void): void => {
|
||||||
|
this.props.httpClient.get(`${siteUrl}/_api/search/query?querytext='WorkId:${actorId}'&selectproperties='DocId,Title,WorkEmail'&ClientType='MyRecentDocuments'&SourceId='b09a7990-05ea-4af9-81ef-edfab16c4e31'`, {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json;odata=nometadata',
|
||||||
|
'odata-version': ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response: Response): Promise<ISearchQueryResponse> => {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((response: ISearchQueryResponse): Promise<IActorInformation> => {
|
||||||
|
if (!response ||
|
||||||
|
!response.PrimaryQueryResult ||
|
||||||
|
!response.PrimaryQueryResult.RelevantResults ||
|
||||||
|
response.PrimaryQueryResult.RelevantResults.RowCount === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cells: ICell[] = response.PrimaryQueryResult.RelevantResults.Table.Rows[0].Cells;
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
email: SearchUtils.getValueFromResults('WorkEmail', cells),
|
||||||
|
id: parseInt(SearchUtils.getValueFromResults('DocId', cells)),
|
||||||
|
name: SearchUtils.getValueFromResults('Title', cells),
|
||||||
|
photoUrl: Utils.getUserPhotoUrl(SearchUtils.getValueFromResults('WorkEmail', cells), siteUrl)
|
||||||
|
});
|
||||||
|
}, (error: any): void => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Manage the settings of this Web Part",
|
||||||
|
"ViewGroupName": "View",
|
||||||
|
"NumberOfDocumentsFieldLabel": "Number of documents to show",
|
||||||
|
"TitleFieldLabel": "Web Part Title (displayed in the body)"
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
declare interface IMyRecentDocumentsStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
ViewGroupName: string;
|
||||||
|
NumberOfDocumentsFieldLabel: string;
|
||||||
|
TitleFieldLabel: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'myRecentDocumentsStrings' {
|
||||||
|
const strings: IMyRecentDocumentsStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import * as assert from 'assert';
|
||||||
|
|
||||||
|
describe('MyRecentDocumentsWebPart', () => {
|
||||||
|
it('should do something', () => {
|
||||||
|
assert.ok(true);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue